匹配属性和元素的正确XPath语法是什么?
更多信息
我创建了以下函数来查找包含给定值的元素和属性:
function Get-XPathToValue {
[CmdletBinding()]
param (
[Parameter(Mandatory)]
[xml]$Xml
,
[Parameter(Mandatory)]
[string]$Value
)
process {
$Xml.SelectNodes("//*[.='{0}']" -f ($Value -replace "'","''")) | %{
$xpath = ''
$elem = $_
while (($elem -ne $null) -and ($elem.NodeType -ne 'Document')) {
$xpath = '/' + $elem.Name + $xpath
$elem = $elem.SelectSingleNode('..')
}
$xpath
}
}
}
这匹配元素,但不匹配属性。
将$Xml.SelectNodes("//*[.='{0}']"
替换为$Xml.SelectNodes("//@*[.='{0}']"
我可以匹配属性,但不匹配元素。
示例
[xml]$sampleXml = @"
<root>
<child1>
<child2 attribute1='hello'>
<ignoreMe>what</ignoreMe>
<child3>hello</child3>
<ignoreMe2>world</ignoreMe2>
</child2>
<child2Part2 attribute2="ignored">hello</child2Part2>
</child1>
<notMe>
<norMe>Not here</norMe>
</notMe>
</root>
"@
Get-XPathToValue -Xml $sampleXml -Value 'hello'
返回:
/root/child1/child2/child3
/root/child1/child2Part2
应该回归:
/root/child1/child2/attribute1
/root/child1/child2/child3
/root/child1/child2Part2
您尝试了什么?
我尝试过匹配:
//@*|*[.='{0}']
- 返回匹配的元素,但返回所有属性。//*|@*[.='{0}']
- 返回匹配的属性,但返回所有元素。//*[.='{0}']|@*[.='{0}']"
- 返回匹配的元素。//@*[.='{0}']|*[.='{0}']"
- 返回匹配的属性。//(@*|*)[.='{0}']"
- 抛出异常。答案 0 :(得分:1)
使用以下XPath解决了问题:function Get-XPathToValue {
[CmdletBinding()]
param (
[Parameter(Mandatory)]
[xml]$Xml
,
[Parameter(Mandatory)]
[string]$Value
)
process {
$Xml.SelectNodes("//@*[.='{0}']|//*[./text()='{0}']" -f ($Value -replace "'","''")) | %{
$xpath = ''
$elem = $_
while (($elem -ne $null) -and ($elem.NodeType -ne 'Document')) {
$prefix = ''
if($elem.NodeType -eq 'Attribute'){$prefix = '@'}
$xpath = '/' + $prefix + $elem.Name + $xpath
$elem = $elem.SelectSingleNode('..')
}
$xpath
}
}
}
即
n
答案 1 :(得分:1)
您的推导XPath表达式的方法有三个缺陷,如您对问题的评论中所示。
这是我对一个解决这些问题的函数的看法(我还给了它一个我认为在cmdlet命名方案中更合适的名称):
function Convert-ValueToXpath {
[CmdletBinding()]
param (
[Parameter(Mandatory)]
[xml]$Xml
,
[Parameter(Mandatory)]
[string]$Value
)
process {
$escapedValue = "concat('', '" + ($value -split "'" -join "', ""'"", '") + "')"
$Xml.SelectNodes("(//*|//@*)[normalize-space() = {0}]" -f $escapedValue) | % {
$xpath = ''
$elem = $_
while ($true) {
if ($elem.NodeType -eq "Attribute") {
$xpath = '/@' + $elem.Name
$elem = $elem.OwnerElement
} elseif ($elem.ParentNode) {
$precedingExpr = "./preceding-sibling::*[local-name() = '$($elem.LocalName)' and namespace-uri() = '$($elem.NamespaceURI)']"
$pos = $elem.SelectNodes($precedingExpr).Count + 1
$xpath = '/' + $elem.Name + "[" + $pos + "]" + $xpath
$elem = $elem.ParentNode
} else {
break;
}
}
$xpath
}
}
}
对于您的示例输入,我得到了这些XPath:
/root[1]/child1[1]/child2[1]/@attribute1 /root[1]/child1[1]/child2[1]/child3[1] /root[1]/child1[1]/child2Part2[1]