使用PowerShell解析带名称空间的XML
问题描述:
我需要一些帮助来理解PowerShell中的XML。 我有几个XML文件是这样的:使用PowerShell解析带名称空间的XML
<?xml version="1.0" encoding="UTF-8"?>
<catalog xmlns="http://www.example.com/xml/catalog/2006-10-31">
<product product-id="11210">
...
<available-flag>true</available-flag>
<online-flag>false</online-flag>
<online-flag site-id="ru">true</online-flag>
<online-flag site-id="fr">true</online-flag>
<online-flag site-id="uk">false</online-flag>
<online-flag site-id="de">true</online-flag>
...
</product>
<product product-id="50610">
...
<available-flag>true</available-flag>
<online-flag>true</online-flag>
<online-flag site-id="ru">false</online-flag>
<online-flag site-id="fr">true</online-flag>
<online-flag site-id="uk">false</online-flag>
<online-flag site-id="de">fasle</online-flag>
...
</product>
<product product-id="82929">
...
<available-flag>true</available-flag>
<online-flag>true</online-flag>
<online-flag site-id="ru">false</online-flag>
<online-flag site-id="fr">true</online-flag>
<online-flag site-id="uk">false</online-flag>
<online-flag site-id="de">true</online-flag>
...
</product>
</catalog>
我需要两个元素的值在PowerShell中:
-
<online-flag>
(不site-id
属性) <online-flag site-id="ru">
对于带有product-id="50610"
的产品。
我有以下代码:
$Path = "C:\Temp\0\2017-08-12_190211.xml"
$XPath = "/ns:catalog/ns:product[@product-id='50610']"
$files = Get-ChildItem $Path | Where {-not $_.PSIsContainer}
if ($files -eq $null) {
return
}
foreach ($file in $files) {
[xml]$xml = Get-Content $file
$namespace = $xml.DocumentElement.NamespaceURI
$ns = New-Object System.Xml.XmlNamespaceManager($xml.NameTable)
$ns.AddNamespace("ns", $namespace)
$product = $xml.SelectSingleNode($XPath, $ns)
}
几个问题:
-
有了这个代码,我可以选择所需要的产品节点。 PowerShell中显示:
online-flag : {true, online-flag, online-flag, online-flag...}
但如何然后我可以选择所需要的
online-flag
元素的值(如果有可能两种方式:一个的XPath和对象之一)? -
是否可以用“对象”方式选择一个节点?就像这样:
$product = $xml.catalog.product | Where-Object {$_."product-id".value -eq "50610"}
如果我有几个文件,究竟是选择文件名,全球在线标志(无属性),具体的网上旗的最佳方式?
答
使用两个不同的XPath表达式:
-
用于选择节点,而无需特定的属性:
//ns:product[@product-id='50610']/ns:online-flag[not(@site-id)]
-
用于选择节点具有特定属性值:
//ns:product[@product-id='50610']/ns:online-flag[@site-id='ru']
$XPath = "/ns:catalog/ns:product[@product-id='50610']"
...
$product = $xml.SelectSingleNode($XPath, $ns)
$product.SelectSingleNode("./ns:online-flag[not(@site-id)]", $ns)
$product.SelectSingleNode("./ns:online-flag[@site-id='ru']", $ns)
如果你需要得到包括文件名的数据和两个节点的值,我建议:您可以通过相对XPath表达式当前节点(.
)选择相对于已经选择的节点的节点建设自定义对象:
$files | ForEach-Object {
[xml]$xml = Get-Content $_
...
New-Object -Type PSObject -Property @{
'Filename' = $_
'online' = $product.SelectSingleNode("./ns:online-flag[not(@site-id)]", $ns).'#text'
'ru_online' = $product.SelectSingleNode("./ns:online-flag[@site-id='ru']", $ns).'#text'
}
}
使用点符号和过滤通过Where-Object
应该是可能的,但我不会推荐它。我发现XPath更加高效。
答
我能得到我需要的“对象”的方式中的数据:
$product = $xml.catalog.product | Where-Object {$_."product-id" -eq "50610"}
$of = $product."online-flag"
$glblsid = $of | Where-Object {$_ -is [System.String]}
$specsid = ($of | Where-Object {$_."site-id" -eq "ru"})."#text"
但我不喜欢,我设法做到这一点的方式。有更方便的解决方案吗?
第二个问题的答案是肯定的 - 请参阅第一行。
答
要完成此主题。我测量了3种方法的性能:点样式,文件上的XPath和节点上的XPath。他们之间没有显着差异。 以下是详细信息。
我解析了2次2个文件,每个60MB。
-
对象样式(点样式)
... $StartTime = Get-Date foreach ($file in $files) { [xml]$xml = Get-Content $file #Object style $product = $xml.catalog.product | Where-Object {$_."product-id" -eq "50610"} $of = $product."online-flag" $glblsid = $of | Where-Object {$_ -is [System.String]} $specsid = ($of | Where-Object {$_."site-id" -eq "ru"})."#text" Write-Output "$($file.Name) $glblsid $specsid" } $EndTime = Get-Date $TimeSpan = New-TimeSpan -Start $StartTime -End $EndTime Write-Output $TimeSpan.TotalMilliseconds
结果:
... $StartTime = Get-Date foreach ($file in $files) { [xml]$xml = Get-Content $file #XPath on the file $namespace = $xml.DocumentElement.NamespaceURI $ns = New-Object System.Xml.XmlNamespaceManager($xml.NameTable) $ns.AddNamespace("ns", $namespace) $glblsid = $xml.SelectSingleNode("/ns:catalog/ns:product[@product-id='50610']/ns:online-flag[not(@site-id)]", $ns).'#text' $specsid = $xml.SelectSingleNode("/ns:catalog/ns:product[@product-id='50610']/ns:online-flag[@site-id='ru']", $ns).'#text' Write-Output "$($file.Name) $glblsid $specsid" } $EndTime = Get-Date $TimeSpan = New-TimeSpan -Start $StartTime -End $EndTime Write-Output $TimeSpan.TotalMilliseconds
结果:
PS> .\ParseXML2.ps1 2017-08-10_190159.xml false false 2017-08-11_190203.xml false true 36269,535 PS> .\ParseXML2.ps1 2017-08-10_190159.xml false false 2017-08-11_190203.xml false true 36628,3304
-
上的文件的XPath:
PS> .\ParseXML2.ps1 2017-08-10_190159.xml false false 2017-08-11_190203.xml false true 36129,1368 PS> .\ParseXML2.ps1 2017-08-10_190159.xml false false 2017-08-11_190203.xml false true 38890,3014
-
的XPath的节点上:
... $StartTime = Get-Date foreach ($file in $files) { [xml]$xml = Get-Content $file #XPath on the node $namespace = $xml.DocumentElement.NamespaceURI $ns = New-Object System.Xml.XmlNamespaceManager($xml.NameTable) $ns.AddNamespace("ns", $namespace) $product = $xml.SelectSingleNode("/ns:catalog/ns:product[@product-id='50610']", $ns) $glblsid = $product.SelectSingleNode("ns:online-flag[not(@site-id)]", $ns).'#text' $specsid = $product.SelectSingleNode("ns:online-flag[@site-id='ru']", $ns).'#text' Write-Output "$($file.Name) $glblsid $specsid" } $EndTime = Get-Date $TimeSpan = New-TimeSpan -Start $StartTime -End $EndTime Write-Output $TimeSpan.TotalMilliseconds
结果:
PS> .\ParseXML2.ps1 2017-08-10_190159.xml false false 2017-08-11_190203.xml false true 33477,1708 PS> .\ParseXML2.ps1 2017-08-10_190159.xml false false 2017-08-11_190203.xml false true 34116,7626
安斯加尔您好!感谢您的回答。我已经提到这个点符号正在工作,我同意这不方便。你的例子的问题是我的XML文件很大,选择两个节点需要时间。首先可以像我的示例中那样选择产品,然后使用XPath选择联机标记元素的值?在这种情况下,XPath会是什么? – Alterant
我尝试了所有以下没有运气: $ product.SelectSingleNode(“/ ns:product/ns:online-flag [@ site-id ='ru']”,$ ns), $ product.SelectSingleNode(“/ns:online-flag [@ site-id ='ru']“,$ ns), $,product_SelectSingleNode(”/ product/online-flag [@ site-id ='ru']“), $ product.SelectSingleNode( “/在线标志[@站点-ID = 'RU']”)。 This $ product。GetElementsByTagName(“在线标志”)的作品。但结果不是一个单一的价值,而是一个价值清单。 – Alterant
在这里找到答案:https://stackoverflow.com/questions/2238201/xmlnode-selectsinglenode-returns-element-outside-current – Alterant