代码之家  ›  专栏  ›  技术社区  ›  Luis Filipe

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

  •  37
  • Luis Filipe  · 技术社区  · 16 年前

    我有一个XML文件,开始如下:

    <Elements name="Entities" xmlns="XS-GenerationToolElements">
    

    我得打开很多这些文件。每个名称空间都不同,但一次只能有一个名称空间(在一个XML文件中永远找不到定义的两个名称空间)。

    使用xpath,我想有一种自动的方法将给定的名称空间添加到名称空间管理器中。 到目前为止,我只能通过解析XML文件来获取名称空间,但是我有一个xpathnavigator实例,它应该有一个好的、干净的方法来获取名称空间,对吗?

    --或者——

    假设我只有一个名称空间,不知何故让xpath使用XML中唯一存在的名称空间,从而避免总是附加名称空间而导致代码混乱。

    3 回复  |  直到 7 年前
        1
  •  83
  •   Richard    14 年前

    您可能会尝试一些技术;您使用哪些技术将完全取决于您需要从文档中获得什么信息、您想要的严格程度以及您使用的XPath实现的一致性。

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

    /*/namespace::*[name()='']
    

    您可以使用它为XpathNavigator设置命名空间关联。不过,请注意, 命名空间:: axis是xpath 1.0的一个角,并不总是实现的。

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

    namespace-uri(/*)
    

    将为您提供该命名空间。

    另一种选择是忘记将前缀与该名称空间相关联,只需释放路径名称空间即可。您可以使用 local-name() 每当需要引用不知道其名称空间的元素时,函数都会运行。例如:

    //*[local-name() = 'Element']
    

    如果您真的需要,您可以更进一步,根据文档元素之一测试元素的名称空间URI:

    //*[local-name() = 'Element' and namespace-uri() = namespace-uri(/*)]
    

    最后一个选择是,考虑到名称空间对您来说似乎毫无意义,通过一个除去名称空间的过滤器来运行XML。那么您就不必在XPath中担心它们了。最简单的方法是移除 xmlns 属性,但如果需要同时进行其他整理,则可以执行更复杂的操作。

        2
  •  10
  •   Dimitre Novatchev    11 年前

    这个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>
    

    应用于以下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>
    

    想要的结果是:

    <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>
    
        3
  •  4
  •   skaffman    16 年前

    不幸的是,xpath没有任何“默认名称空间”的概念。您需要使用xpath上下文注册带有前缀的名称空间,然后在xpath表达式中使用这些前缀。它意味着非常详细的xpath,但它是xpath 1的一个基本缺点。显然,xpath 2会解决这个问题,但现在这对您没有用处。

    我建议您以编程方式检查XML文档中的命名空间,将该命名空间与xpath上下文中的前缀关联起来,然后在xpath表达式中使用前缀。