1

Not able to understand why my simple xpath does not work on the below XML.

I need to find the node "deployment" and delete the childnode and then add a new child node. Should be pretty easy, but no, there is something with the XML that makes it not possible for my code to find the deployment-node by simply selecting it by its name.

The XML (Clickonce .Net XML)

<?xml version="1.0" encoding="utf-8"?>
<asmv1:assembly xsi:schemaLocation="urn:schemas-microsoft-com:asm.v1 assembly.adaptive.xsd" manifestVersion="1.0" xmlns:asmv1="urn:schemas-microsoft-com:asm.v1" xmlns="urn:schemas-microsoft-com:asm.v2" xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:xrml="urn:mpeg:mpeg21:2003:01-REL-R-NS" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#" xmlns:co.v1="urn:schemas-microsoft-com:clickonce.v1" xmlns:co.v2="urn:schemas-microsoft-com:clickonce.v2">
  <assemblyIdentity name="PublishDev.app" version="3.1.0.182" publicKeyToken="38f29cb66cf790f2" language="neutral" processorArchitecture="x86" xmlns="urn:schemas-microsoft-com:asm.v1" />
  <description asmv2:publisher="KLP" asmv2:product="PublishDev" xmlns="urn:schemas-microsoft-com:asm.v1" />
  <deployment install="true">
    <subscription>
      <update>
        <expiration maximumAge="0" unit="days" />
      </update>
    </subscription>
    <deploymentProvider codebase="http://nrs.dev/NrsClient/PublishDev.application" />
  </deployment>
  <compatibleFrameworks xmlns="urn:schemas-microsoft-com:clickonce.v2">
    <framework targetVersion="4.7" profile="Client" supportedRuntime="4.0.30319" />
    <framework targetVersion="4.7" profile="Full" supportedRuntime="4.0.30319" />
  </compatibleFrameworks>
  <dependency>
    <dependentAssembly dependencyType="install" codebase="ApplicationFiles\PublishDev_3_1_0_182\PublishDev.exe.manifest" size="48466">
      <assemblyIdentity name="PublishDev.exe" version="3.1.0.182" publicKeyToken="38f29cb66cf790f2" language="neutral" processorArchitecture="x86" type="win32" />
      <hash>
        <dsig:Transforms>
          <dsig:Transform Algorithm="urn:schemas-microsoft-com:HashTransforms.Identity" />
        </dsig:Transforms>
        <dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
        <dsig:DigestValue>zbbwOLiL12PznwEX7G3FGhmU8R0=</dsig:DigestValue>
      </hash>
    </dependentAssembly>
  </dependency>
</asmv1:assembly>

Then to loop the XML using Powershell

function loopXml{
    param(
        $nodes
    )
    write-host "*************************  Loop my xml********************* "
    foreach ($node in $nodes) {
        Write-host $node.toString()
    }
}

$deployManifestFullPath = "D:\temp\stripped.xml"

$sourcexml = [xml](get-content $deployManifestFullPath);
$nodes = $sourcexml.SelectNodes("deployment")
loopXml $nodes 

$nodes = $sourcexml.SelectNodes("//*", $ns)
loopXml $nodes 

The output

*************************  Loop my xml*********************
*************************  Loop my xml*********************
assembly
assemblyIdentity
description
deployment
subscription
update
expiration
deploymentProvider
compatibleFrameworks
framework
framework
dependency
dependentAssembly
assemblyIdentity
hash
Transforms
Transform
DigestMethod
DigestValue

I do not understand why the line

$nodes = $sourcexml.SelectNodes("deployment")

does not return the deployment-node. When I select the whole document it is there, but ... well, need some help to figure out this one.

Ref. w3schools XPath Syntax just adding the name of the node should select all nodes with that name. In my case, the single node named deployment.

0

2 Answers 2

3

Try changing

$nodes = $sourcexml.SelectNodes("deployment")

to

$nodes = $sourcexml.SelectNodes("//*[name()='deployment']")
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks, that did the trick... Confused since W3CSchools says this shold work: nodename Selects all nodes with the name "nodename" But - won't spend to much time on that, thanks again!
0

InvalidArgument: Cannot convert value "System.Object[]" to type "System.Xml.XmlDocument". Error: "Unexpected end of file has occurred. The following elements are not closed: asmv1:assembly. Line 40, position 14."

You are missing something like this at the bottom of the xml:

</asmv1:assembly>

If you indented properly $sourcexml.save('stripped.xml'), you would see that deployment is not the top level node. There's also the namespace problem.

You can get the $nodes this way. There's only one. I don't know namespaces:

$nodes = $sourcexml.assembly.deployment
$nodes 


install subscription deploymentProvider
------- ------------ ------------------
true    subscription deploymentProvider

Hmm, this works:

select-xml asmv1:assembly stripped.xml -Namespace @{asmv1='urn:schemas-microsoft-com:asm.v1'}


Node     Path                           Pattern
----     ----                           -------
assembly /Users/js/foo/stripped.xml     asmv1:assembly

But not this:

select-xml '//deployment' stripped.xml -Namespace @{asmv1='urn:schemas-microsoft-com:asm.v1'} 

Ugh. This works. The x could be anything. It can't be xmlns.

select-xml '//x:deployment' stripped.xml -Namespace @{x='urn:schemas-microsoft-com:asm.v2'}  


Node       Path                           Pattern
----       ----                           -------
deployment /Users/js/foo/stripped.xml     //x:deployment

And finally the selectnodes() version with the namespace. You need the // to search subnodes, because deployment is not the top level node. Having a namespace makes it more tricky. x represents the default namespace here.

[System.Xml.XmlNamespaceManager]$nsmgr = $sourcexml.NameTable
$nsmgr.AddNamespace('x','urn:schemas-microsoft-com:asm.v2')
$nodes = $sourcexml.selectnodes('//x:deployment', $nsmgr)
$nodes


install subscription deploymentProvider
------- ------------ ------------------
true    subscription deploymentProvider

3 Comments

Sorry for that, it was just a simple editing issue - the closing tag was not indented enough, and therefore it was not showing. have updated the code so now it should be just fine.
But - yes deployment is not top level node, but $nodes = $sourcexml.SelectNodes("deployment") is valid xpath if I'm not totally mistaken. It should find any node named deployment anywhere in the document.
It would be $sourcexml.SelectNodes("//deployment"). But there's a namespace too.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.