如何使用Xpath检索XML文件中的命名空间

Lui*_*ipe 37 xml xpath namespaces

我有一个XML文件,如下所示:

<Elements name="Entities" xmlns="XS-GenerationToolElements">
Run Code Online (Sandbox Code Playgroud)

我将不得不打开很多这些文件.每个命名空间都有不同的命名空间,但一次只有一个命名空间(我永远不会在一个xml文件中找到两个命名空间).

使用XPath我想有一种自动方式将给定的命名空间添加到命名空间管理器.到目前为止,我只能通过解析xml文件来获取命名空间,但我有一个XPathNavigator实例,它应该有一个很好的,干净的方式来获取命名空间,对吧?

- 要么 -

鉴于我只有一个命名空间,以某种方式使XPath使用xml中唯一存在的命名空间,从而通过始终附加命名空间来避免混乱代码.

Jen*_*niT 83

您可以尝试一些技巧; 您使用的将取决于您需要从文档中获取哪些信息,您想要的严格程度以及您正在使用的XPath实现的一致性.

获取与特定前缀关联的名称空间URI的一种方法是使用namespace::轴.这将为您提供一个命名空间节点,其名称为前缀,其值为命名空间URI.例如,您可以使用以下路径获取文档元素上的默认名称空间URI:

/*/namespace::*[name()='']
Run Code Online (Sandbox Code Playgroud)

您可以使用它来为XPathNavigator设置名称空间关联.但是要注意,namespace::轴是XPath 1.0的那些并不总是实现的角落之一.

获取该命名空间URI的第二种方法是使用namespace-uri()文档元素上的函数(您已经说过该函数将始终位于该命名空间中).表达方式:

namespace-uri(/*)
Run Code Online (Sandbox Code Playgroud)

将为您提供该命名空间.

另一种方法是忘记将前缀与该命名空间相关联,并使您的路径无名称空间.您可以local-name()在需要引用您不知道其命名空间的元素时使用该函数来执行此操作.例如:

//*[local-name() = 'Element']
Run Code Online (Sandbox Code Playgroud)

您可以更进一步,如果您真的想要,则针对文档元素之一测试元素的名称空间URI:

//*[local-name() = 'Element' and namespace-uri() = namespace-uri(/*)]
Run Code Online (Sandbox Code Playgroud)

鉴于命名空间似乎对您没有任何意义,最后一个选项是通过剥离名称空间的过滤器来运行XML.然后你根本不必在XPath中担心它们.最简单的方法是xmlns使用正则表达式删除属性,但如果您需要同时进行其他整理,则可以执行更复杂的操作.

  • 仅供参考:在XPath版本2.0中,[名称空间轴已弃用](http://www.w3.org/TR/xpath20/#dt-in-scope-namespaces). (3认同)
  • 非常感谢您的详细解答我似乎没有声誉可以投票给您 (2认同)

Dim*_*hev 10

这个40行xslt转换提供了有关给定XML文档中命名空间的所有有用信息:

<xsl:stylesheet version="1.0"
   xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
   xmlns:ext="http://exslt.org/common"
   exclude-result-prefixes="ext"
>

<xsl:output omit-xml-declaration="yes" indent="yes"/>

<xsl:strip-space elements="*"/>

<xsl:key name="kNsByNsUri" match="ns" use="@uri"/>

<xsl:variable name="vXmlNS" 
    select="'http://www.w3.org/XML/1998/namespace'"/>

<xsl:template match="/">
  <xsl:variable name="vrtfNamespaces">
    <xsl:for-each select=
      "//namespace::*
             [not(. = $vXmlNS)
             and
              . = namespace-uri(..)
           ]">
      <ns element="{name(..)}"
          prefix="{name()}" uri="{.}"/>
    </xsl:for-each>
  </xsl:variable>

  <xsl:variable name="vNamespaces"
    select="ext:node-set($vrtfNamespaces)/*"/>

  <namespaces>
          <xsl:for-each select=
           "$vNamespaces[generate-id()
                        =
                         generate-id(key('kNsByNsUri',@uri)[1])
                        ]">
            <namespace uri="{@uri}">
              <xsl:for-each select="key('kNsByNsUri',@uri)/@element">
                <element name="{.}" prefix="{../@prefix}"/>
              </xsl:for-each>
            </namespace>
          </xsl:for-each>
  </namespaces>
</xsl:template>
Run Code Online (Sandbox Code Playgroud)

应用于以下XML文档时:

<a xmlns="my:def1" xmlns:n1="my:n1"
   xmlns:n2="my:n2" xmlns:n3="my:n3">
  <b>
    <n1:d/>
  </b>
  <n1:c>
    <n2:e>
      <f/>
    </n2:e>
  </n1:c>
  <n2:g/>
</a>
Run Code Online (Sandbox Code Playgroud)

产生了想要的结果:

<namespaces>
   <namespace uri="my:def1">
      <element name="a" prefix=""/>
      <element name="b" prefix=""/>
      <element name="f" prefix=""/>
   </namespace>
   <namespace uri="my:n1">
      <element name="n1:d" prefix="n1"/>
      <element name="n1:c" prefix="n1"/>
   </namespace>
   <namespace uri="my:n2">
      <element name="n2:e" prefix="n2"/>
      <element name="n2:g" prefix="n2"/>
   </namespace>
</namespaces>
Run Code Online (Sandbox Code Playgroud)