如何使用Xpath检索XML文件中的命名空间
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何使用Xpath检索XML文件中的命名空间相关的知识,希望对你有一定的参考价值。
我有一个XML文件,如下所示:
<Elements name="Entities" xmlns="XS-GenerationToolElements">
我将不得不打开很多这些文件。每个命名空间都有不同的命名空间,但一次只有一个命名空间(我永远不会在一个xml文件中找到两个命名空间)。
使用XPath我想有一种自动方式将给定的命名空间添加到命名空间管理器。到目前为止,我只能通过解析xml文件来获取命名空间,但我有一个XPathNavigator实例,它应该有一个很好的,干净的方式来获取命名空间,对吧?
- 要么 -
鉴于我只有一个命名空间,以某种方式使XPath使用xml中唯一存在的命名空间,从而通过总是附加命名空间来避免混乱代码。
您可以尝试一些技巧;您使用的将取决于您需要从文档中获取哪些信息,您想要的严格程度以及您正在使用的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
属性,但如果你需要同时进行其他整理,你可以做一些更复杂的事情。
这个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>
不幸的是,XPath没有任何“默认命名空间”的概念。您需要使用XPath上下文注册带前缀的名称空间,然后在XPath表达式中使用这些前缀。这意味着非常详细的xpath,但它是XPath 1的一个基本缺点。显然XPath 2将解决这个问题,但这对你现在没用。
我建议您以编程方式检查命名空间的XML文档,将该命名空间与XPath上下文中的前缀相关联,然后在xpath表达式中使用前缀。
以上是关于如何使用Xpath检索XML文件中的命名空间的主要内容,如果未能解决你的问题,请参考以下文章
查找 XML 文档中的所有命名空间声明 - xPath 1.0 与 xPath 2.0
XPATH 帮助:使用 XPathNodeIterator 在命名空间中查找 XML 节点