PowerShell XML select 具有命名空间的节点
PowerShell XML select node with namespace
我正在尝试 select 来自 XML 文件的节点,但我遇到了似乎是由命名空间引起的问题。
下面的代码没有 return 任何东西。但是,如果我从 XML 文件中删除名称空间,我将获得预期的结果。
MWE
$StandaloneXML = "test.xml"
# Load XML content
$NewStandaloneXML = New-Object -TypeName "System.XML.XMLDocument"
$NewStandaloneXML.Load($StandaloneXML)
# Get namespace
$Namespace = New-Object -TypeName "Xml.XmlNamespaceManager" -ArgumentList $NewStandaloneXML.NameTable
$Namespace.AddNamespace("jboss", $NewStandaloneXML.DocumentElement.NamespaceURI)
$NewStandaloneXML.SelectNodes("jboss:server/interfaces/interface", $Namespace)
XML
<?xml version="1.0" ?>
<server xmlns="urn:jboss:domain:4.2">
<interfaces>
<interface name="management">
<inet-address value="${jboss.bind.address.management:127.0.0.1}"/>
</interface>
<interface name="public">
<inet-address value="${jboss.bind.address:127.0.0.1}"/>
</interface>
</interfaces>
</server>
预期输出
名称 inet 地址
----------------
管理 inet 地址
public inet 地址
正如@AnsgarWiechers 所述,每个节点都必须以其名称空间为前缀,因为没有继承。
MWE
$StandaloneXML = "test.xml"
# Load XML content
$NewStandaloneXML = New-Object -TypeName "System.XML.XMLDocument"
$NewStandaloneXML.Load($StandaloneXML)
# Get namespace
$Namespace = New-Object -TypeName "Xml.XmlNamespaceManager" -ArgumentList $NewStandaloneXML.NameTable
$Namespace.AddNamespace("jboss", $NewStandaloneXML.DocumentElement.NamespaceURI)
$NewStandaloneXML.SelectNodes("jboss:server/jboss:interfaces/jboss:interface", $Namespace)
为了让事情更简单,我构建了一个小函数来自动为所提供的 XPath
中的每个节点添加前缀。
function Select-XMLNode {
[CmdletBinding()]
Param (
[Parameter (
Position = 1,
Mandatory = $true,
HelpMessage = "XML content"
)]
[ValidateNotNullOrEmpty()]
[System.XML.XMLDocument]
$XML,
[Parameter (
Position = 2,
Mandatory = $true,
HelpMessage = "XPath corresponding to the node"
)]
[ValidateNotNullOrEmpty()]
[String]
$XPath,
[Parameter (
Position = 3,
Mandatory = $false,
HelpMessage = "Namespace"
)]
[ValidateNotNullOrEmpty()]
[String]
$Namespace = $XML.DocumentElement.NamespaceURI
)
Begin {
# Variables
$Delimiter = "/"
$Alias = "x"
$SpecialCharacters = [RegEx]::New('^[/.@]*')
if ($XPath -match $SpecialCharacters) {
$Prefix = $Matches[0]
$XPath = $XPath -replace $SpecialCharacters, ''
}
}
Process {
# Get namespace
$NamespaceManager = New-Object -TypeName "Xml.XmlNamespaceManager" -ArgumentList $XML.NameTable
$NamespaceManager.AddNamespace($Alias, $Namespace)
# Split XPath to identify nodes
$Nodes = $XPath.Split($Delimiter)
$PrefixedNodes = New-Object -TypeName "System.Collections.ArrayList"
# Prefix nodes with namespace (alias)
foreach($Node in $Nodes) {
if ($Node) {
[Void]$PrefixedNodes.Add("${Alias}:${Node}")
}
}
# Join prefixed-nodes to create new XPath with namespace
$XPathWithNamespace = $PrefixedNodes -join $Delimiter
# Check XPath prefix
if ($Prefix) {
$XPathWithNamespace = $Prefix + "" + $XPathWithNamespace
}
# Select and return nodes
$SelectedNodes = $XML.SelectNodes($XPathWithNamespace, $NamespaceManager)
return $SelectedNodes
}
}
我正在尝试 select 来自 XML 文件的节点,但我遇到了似乎是由命名空间引起的问题。
下面的代码没有 return 任何东西。但是,如果我从 XML 文件中删除名称空间,我将获得预期的结果。
MWE
$StandaloneXML = "test.xml"
# Load XML content
$NewStandaloneXML = New-Object -TypeName "System.XML.XMLDocument"
$NewStandaloneXML.Load($StandaloneXML)
# Get namespace
$Namespace = New-Object -TypeName "Xml.XmlNamespaceManager" -ArgumentList $NewStandaloneXML.NameTable
$Namespace.AddNamespace("jboss", $NewStandaloneXML.DocumentElement.NamespaceURI)
$NewStandaloneXML.SelectNodes("jboss:server/interfaces/interface", $Namespace)
XML
<?xml version="1.0" ?>
<server xmlns="urn:jboss:domain:4.2">
<interfaces>
<interface name="management">
<inet-address value="${jboss.bind.address.management:127.0.0.1}"/>
</interface>
<interface name="public">
<inet-address value="${jboss.bind.address:127.0.0.1}"/>
</interface>
</interfaces>
</server>
预期输出
名称 inet 地址 ---------------- 管理 inet 地址 public inet 地址
正如@AnsgarWiechers 所述,每个节点都必须以其名称空间为前缀,因为没有继承。
MWE
$StandaloneXML = "test.xml"
# Load XML content
$NewStandaloneXML = New-Object -TypeName "System.XML.XMLDocument"
$NewStandaloneXML.Load($StandaloneXML)
# Get namespace
$Namespace = New-Object -TypeName "Xml.XmlNamespaceManager" -ArgumentList $NewStandaloneXML.NameTable
$Namespace.AddNamespace("jboss", $NewStandaloneXML.DocumentElement.NamespaceURI)
$NewStandaloneXML.SelectNodes("jboss:server/jboss:interfaces/jboss:interface", $Namespace)
为了让事情更简单,我构建了一个小函数来自动为所提供的 XPath
中的每个节点添加前缀。
function Select-XMLNode {
[CmdletBinding()]
Param (
[Parameter (
Position = 1,
Mandatory = $true,
HelpMessage = "XML content"
)]
[ValidateNotNullOrEmpty()]
[System.XML.XMLDocument]
$XML,
[Parameter (
Position = 2,
Mandatory = $true,
HelpMessage = "XPath corresponding to the node"
)]
[ValidateNotNullOrEmpty()]
[String]
$XPath,
[Parameter (
Position = 3,
Mandatory = $false,
HelpMessage = "Namespace"
)]
[ValidateNotNullOrEmpty()]
[String]
$Namespace = $XML.DocumentElement.NamespaceURI
)
Begin {
# Variables
$Delimiter = "/"
$Alias = "x"
$SpecialCharacters = [RegEx]::New('^[/.@]*')
if ($XPath -match $SpecialCharacters) {
$Prefix = $Matches[0]
$XPath = $XPath -replace $SpecialCharacters, ''
}
}
Process {
# Get namespace
$NamespaceManager = New-Object -TypeName "Xml.XmlNamespaceManager" -ArgumentList $XML.NameTable
$NamespaceManager.AddNamespace($Alias, $Namespace)
# Split XPath to identify nodes
$Nodes = $XPath.Split($Delimiter)
$PrefixedNodes = New-Object -TypeName "System.Collections.ArrayList"
# Prefix nodes with namespace (alias)
foreach($Node in $Nodes) {
if ($Node) {
[Void]$PrefixedNodes.Add("${Alias}:${Node}")
}
}
# Join prefixed-nodes to create new XPath with namespace
$XPathWithNamespace = $PrefixedNodes -join $Delimiter
# Check XPath prefix
if ($Prefix) {
$XPathWithNamespace = $Prefix + "" + $XPathWithNamespace
}
# Select and return nodes
$SelectedNodes = $XML.SelectNodes($XPathWithNamespace, $NamespaceManager)
return $SelectedNodes
}
}