Skip to main content
 首页 » 编程设计

xml之具有命名空间的 PowerShell XML 选择节点

2025年05月04日157me-sa

我尝试从 XML 文件中选择节点,但遇到了似乎由命名空间引起的问题。

下面的代码不返回任何内容。但是如果我从 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> 

预期输出

name       inet-address 
----       ------------ 
management inet-address 
public     inet-address 

请您参考如下方法:

正如 @AnsgarWiechers 所说,每个节点都必须以其 namespace 为前缀,因为没有继承。

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 
  } 
}