我有源XML,它有两个根节点的子节点。我将每个子节点作为 [xml.xmlNodeList] 传递给函数。 在该函数中,当我查看子节点的计数时,它是正确的,1 在第一次调用中传递初始化节点,2 在第二次调用中使用处理节点。但是,如果我随后尝试选择子节点的孙节点,则计数将全部出错。在初始化的windows节点中只有5个替换节点,在处理中只有6个替换节点,但我看到的结果始终是11和22。然而,实际的 XML 是正确的,如 $nodesToAdd 的写入控制台所示。 我去哪儿了这么可怕的错误?我没有看到任何可能污染管道的东西,这是我通常的绊脚石。
$sourceXML = [xml] @"
<tokens>
<initialization>
<windows>
<replacement id="psVersion" type="psVersiontable">psVersion</replacement>
<replacement id="osID" type="regProperty" os="10.0">HKLMSOFTWAREMicrosoftWindows NTCurrentVersionReleaseId</replacement>
<replacement id="osName" type="regProperty">HKLMSOFTWAREMicrosoftWindows NTCurrentVersionProductName</replacement>
<replacement id="osBuild" type="regProperty">HKLMSOFTWAREMicrosoftWindows NTCurrentVersionCurrentBuildNumber</replacement>
<replacement id="osVersion" type="regProperty">HKLMSOFTWAREMicrosoftWindows NTCurrentVersionCurrentVersion</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子级的所有替换节点。所以我尝试了这个,获取传递节点的父节点,并使用它来优化选择。
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."
这种倍增与子节点的数量有关。如果我在处理下添加第三个子节点,我会得到"处理处理处理"作为父节点的名称。
这个想法是只传递我真正想要使用的节点,并减少参数的数量。如果我传递整个XML和我要从中绘制的节点的名称(初始化或处理),我就可以让它工作。只是好奇为什么 xmlNodeList 以这种方式运行,以及是否有办法以某种方式获取单个父节点并使用更少的参数使其工作。
编辑:根据安斯加尔,我现在有这个
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 根节点下方的任何位置,而不仅仅是在传递给函数的节点下方。用于表示"在当前节点下方"的正确 XPath 表达式是./
。
另外,您可能希望将父节点(<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'))
代码将完成您的期望。