如何使用 Xpath 检索 XML 文件中的命名空间
Posted
技术标签:
【中文标题】如何使用 Xpath 检索 XML 文件中的命名空间【英文标题】:How to retrieve namespaces in XML files using Xpath 【发布时间】:2010-09-12 10:56:09 【问题描述】:我有一个这样开头的 XML 文件:
<Elements name="Entities" xmlns="XS-GenerationToolElements">
我必须打开很多这样的文件。它们中的每一个都有不同的命名空间,但一次只能有一个命名空间(我永远不会在一个 xml 文件中找到两个命名空间)。
使用 XPath 我希望有一种自动方式将给定的命名空间添加到命名空间管理器。 到目前为止,我只能通过解析 xml 文件来获取命名空间,但我有一个 XPathNavigator 实例,它应该有一种很好且干净的方式来获取命名空间,对吧?
-- 或者--
鉴于我只有一个命名空间,以某种方式让 XPath 使用 xml 中唯一存在的命名空间,从而通过始终附加命名空间来避免代码混乱。
【问题讨论】:
这些总是在默认命名空间中吗?或者你有没有: xmlns:myns="namespace-uri" 你是否将文件完整地读入 DOM 文档或使用 XmlValidatingReader 之类的东西进行解析? 它们总是在默认命名空间中。我还没有完全阅读文件,因为我陷入了这个问题;我想当您询问“进入 DOM 文档或使用 XmlValidatingReader 之类的东西进行解析”时,我并不完全理解;我只会使用 XPath 来读取 XML;这很糟糕吗? 【参考方案1】:这个 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>
</xsl:stylesheet>
应用于以下 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>
【讨论】:
【参考方案2】:您可以尝试一些技巧;您使用的具体取决于您需要从文档中获取哪些信息、您希望做到的严格程度以及您使用的 XPath 实现的一致性程度。
获取与特定前缀关联的命名空间 URI 的一种方法是使用 namespace::
轴。这将为您提供一个名称空间节点,其名称是前缀,其值是名称空间 URI。例如,您可以使用以下路径获取文档元素上的默认命名空间 URI:
/*/namespace::*[name()='']
您也许可以使用它为您的 XPathNavigator 设置名称空间关联。但请注意,namespace::
轴是 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
属性,但如果您需要同时进行其他整理,您可以做一些更复杂的事情。
【讨论】:
非常感谢您的详细回答,看来我还没有声望给您投票 第二种方法在带有 QXmlQuery 的 Qt 中效果很好。很好的答案。 谢谢,现在我可以在变量中使用 put 命名空间并使用它来创建元素。当命名空间 URI 随产品版本而变化但更改的节点相当稳定时很方便。例如不幸的是,XPath 没有任何“默认命名空间”的概念。您需要在 XPath 上下文中注册带有前缀的名称空间,然后在 XPath 表达式中使用这些前缀。这意味着非常冗长的 xpath,但它是 XPath 1 的一个基本缺点。显然 XPath 2 会解决这个问题,但现在对你没有用。
我建议您以编程方式检查 XML 文档中的命名空间,将该命名空间与 XPath 上下文中的前缀相关联,然后在 xpath 表达式中使用该前缀。
【讨论】:
看来它必须归结为......!谢谢 我怀疑这是实际的答案,因为它似乎是希望避免在 XPath 中查询命名空间的额外复杂性。不要忘记接受适当的答案。以上是关于如何使用 Xpath 检索 XML 文件中的命名空间的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 xpath/php 获取 xml 文件中的节点名称?