xmlNodeList 不限于选择的节点,不提供正确的父节点

xmlNodeList not limited to selected nodes, and not providing correct parent node

我有源 XML,它有根节点的两个子节点。我将这些子节点中的每一个作为 [xml.xmlNodeList] 传递给一个函数。 在该函数中,当我查看子节点的计数时,它是正确的,在第一次调用中为 1,传递初始化节点,在第二次调用中使用处理节点为 2。但是,如果我然后尝试 select 子节点的孙节点,计数就会出错。 windows 节点初始化时只有 5 个替换节点,处理中有 6 个,但我看到的结果始终是 11 和 22。然而实际 XML 是正确的,如 write 所示到 $nodesToAdd 的控制台。 我哪里错得如此可怕?我没有看到任何可能污染管道的东西,这是我通常的绊脚石。

$sourceXML = [xml] @"
<tokens>
  <initialization>
    <windows>
      <replacement id="psVersion" type="psVersiontable">psVersion</replacement>
      <replacement id="osID" type="regProperty" os="10.0">HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ReleaseId</replacement>
      <replacement id="osName" type="regProperty">HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProductName</replacement>
      <replacement id="osBuild" type="regProperty">HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\CurrentBuildNumber</replacement>
      <replacement id="osVersion" type="regProperty">HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\CurrentVersion</replacement>
    </windows>
  </initialization>
  <processing>
    <exitCode>
      <replacement id="successfulExecute" type="string">0, 3010</replacement>
      <replacement id="successfulInstall" type="string">0, 1641, 3010, -2147021886</replacement>
      <replacement id="successfulUninstall" type="string">0, 1641, 3010</replacement>
      <replacement id="wait" type="string">1618, -2147023278</replacement>
    </exitCode>
    <windows>
      <replacement id="commonAppData" type="specialFolder">CommonApplicationData</replacement>
      <replacement id="commonDesktop" type="specialFolder">CommonDesktopDirectory</replacement>
      <replacement id="commonDocuments" type="specialFolder">CommonDocuments</replacement>
      <replacement id="commonProgramFiles" type="specialFolder">CommonProgramFiles</replacement>
      <replacement id="commonProgramFilesX86" type="specialFolder">CommonProgramFilesX86</replacement>
      <replacement id="commonStartMenu" type="specialFolder">CommonStartMenu</replacement>
    </windows>
  </processing>
</tokens>
"@

function Set-PxTokenXml {
    param (
        [xml.xmlNodeList]$nodesToAdd
    )
    Write-PxXmlToConsole $nodesToAdd
    Write-Host "$($nodesToAdd.count)"

    $testNodes = $nodesToAdd.SelectNodes("//windows/*")
    Write-Host "$($testNodes.count)"
}

function Write-PxXmlToConsole ($xml) {
    $stringWriter = New-Object System.IO.StringWriter
    $xmlWriter = New-Object System.Xml.XmlTextWriter $stringWriter
    $xmlWriter.Formatting = "indented"
    $xml.WriteTo($xmlWriter)
    $xmlWriter.Flush()
    $stringWriter.Flush()
    Write-Host $stringWriter.ToString()
    Write-Host
    Write-Host
}

### MAIN
Clear-Host

Set-PxTokenXml ($sourceXML.SelectNodes('//initialization/*'))
Set-PxTokenXml ($sourceXML.SelectNodes('//processing/*'))

貌似$nodesToAdd应该是一个子节点,其实是整个XML,所以"//windows/*"就全部替换了在初始化和处理中都是 windows 的子节点。所以我尝试了这个,获取传递节点的父节点,并使用它来优化 selection.

function Set-PxTokenXml {
    param (
        [xml.xmlNodeList]$nodesToAdd
    )
    #Write-PxXmlToConsole $nodesToAdd
    Write-Host "$($nodesToAdd.count)"
    $parentNode = $nodesToAdd.parentNode.name
    Write-Host "$parentNode"

    $testNodes = $nodesToAdd.SelectNodes("//$parentNode/windows/*")
    Write-Host "$($testNodes.count)"
}

但是那个错误,父节点名称加倍。

Exception calling "SelectNodes" with "1" argument(s): "'//processing processing/windows/*' has an invalid token."

那个翻倍跟子节点的数量有关。如果我在处理中添加第三个子节点,我会得到 'processing processing processing' 作为父节点的名称。

我的想法是只传递我实际想要使用的节点,并减少参数的数量。如果我传递整个 XML 和我想从中绘制的节点的名称(初始化或处理),我可以让它工作。只是好奇为什么 xmlNodeList 会以这种方式运行,以及是否有某种方法可以获取单个父节点并使用更少的参数使其工作。

编辑: Per Ansgar 我现在有这个

function Set-PxTokenXml {
    param (
        [xml.xmlNodeList]$nodesToAdd
    )
    Write-PxXmlToConsole $nodesToAdd
    Write-Host "$($nodesToAdd.count)"

    $testNodes = $nodesToAdd.SelectNodes("./windows/*")
    Write-Host "$($testNodes.count)"
}

现在 $testNodes.count 返回 0。对于这两个调用。 PS5 如果这会有所作为,但我希望不会,因为我需要支持 PS2,至少在代码的早期部分是这样。

XML 对象有点奇怪。您 将选定的子节点传递给 Set-PxTokenXml,但是,这些节点仍然可以访问整个 XML 结构(否则您将无法访问,例如访问它们的父节点)。因此,以 // 开头的 XPath 表达式将查找 XML 根节点下方的任何位置,而不仅仅是传递给函数的节点下方。表达 "below the current node" 的正确 XPath 表达式是 ./.

此外,您可能希望将 parent 节点(<initialization><processing>)传递给 Set-PxTokenXml,而不是这些节点的子节点节点。

换行

$testNodes = $nodesToAdd.SelectNodes("//windows/*")

进入

$testNodes = $nodesToAdd.SelectNodes("./windows/*")

并更改这些行

Set-PxTokenXml ($sourceXML.SelectNodes('//initialization/*'))
Set-PxTokenXml ($sourceXML.SelectNodes('//processing/*'))

进入

Set-PxTokenXml ($sourceXML.SelectNodes('//initialization'))
Set-PxTokenXml ($sourceXML.SelectNodes('//processing'))

并且代码将按照您的预期进行。