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

我有一个XML文件,像这样开始:

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

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

使用XPath我想有一个自动的方式来添加给定的命名空间到命名空间pipe理器。 到目前为止,我只能通过parsingXML文件来获得名称空间,但是我有一个XPathNavigator实例,它应该有一个很好的干净的方式来获取名称空间,对不对?

– 要么 –

鉴于我只有一个命名空间,不知何故使得XPath使用唯一的存在于XML中,从而避免总是附加命名空间混乱的代码。

有几种技巧可以尝试; 您使用哪种方法将取决于您需要从文档中获取哪些信息,您希望的严格程度以及您使用的XPath实现的一致性。

获取与特定前缀关联的命名空间URI的一种方法是使用namespace:: axis。 这会给你一个命名空间节点,其名称是前缀,其值是命名空间URI。 例如,您可以使用以下path获取文档元素上的默认名称空间URI:

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

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

获取该名称空间URI的第二种方法是在文档元素上使用namespace-uri()函数(您所说的将始终在该名称空间中)。 expression方式:

 namespace-uri(/*) 

会给你那个名字空间。

另一种方法是忘记将前缀与该名称空间关联起来,并使path无名称空间。 您可以通过使用local-name()函数来执行此操作,只要需要引用其名称空间不知道的元素即可。 例如:

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

如果你真的想要的话,你可以进一步testing元素的名称空间URI与文档元素的名称空间URI:

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

最后一个选项,假定命名空间对你来说没有任何意义,那么将通过一个filter来运行你的XML,从而去除命名空间。 那么你根本就不用担心XPath中的这些问题。 最简单的方法就是简单地用正则expression式去除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上下文注册带有前缀的名称空间,然后在XPathexpression式中使用这些前缀。 这意味着非常详细的xpath,但这是XPath 1的一个基本缺点。显然,XPath 2将解决这个问题,但是现在对你来说没有用处。

我build议你以编程的方式检查你的XML文档的名称空间,将该名称空间与XPath上下文中的前缀相关联,然后在xpathexpression式中使用前缀。