如何使用 Xpath 检索 XML 文件中的命名空间
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/122463/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to retrieve namespaces in XML files using Xpath
提问by Luis Filipe
I have an XML file that starts like this:
我有一个像这样开头的 XML 文件:
<Elements name="Entities" xmlns="XS-GenerationToolElements">
I'll have to open a lot of these files. Each of these have a different namespace but will only have one namespace at a time (I'll never find two namespaces defined in one xml file).
我将不得不打开很多这样的文件。每一个都有不同的命名空间,但一次只有一个命名空间(我永远不会在一个 xml 文件中找到两个定义的命名空间)。
Using XPath I'd like to have an automatic way to add the given namespace to the namespace manager. So far, i could only get the namespace by parsing the xml file but I have a XPathNavigator instance and it should have a nice and clean way to get the namespaces, right?
使用 XPath 我想有一种自动的方式将给定的命名空间添加到命名空间管理器。到目前为止,我只能通过解析 xml 文件来获取命名空间,但我有一个 XPathNavigator 实例,它应该有一种很好且干净的方式来获取命名空间,对吧?
-- OR --
- 或者 -
Given that I only have one namespace, somehow make XPath use the only one that is present in the xml, thus avoiding cluttering the code by always appending the namespace.
鉴于我只有一个命名空间,以某种方式使 XPath 使用 xml 中唯一存在的命名空间,从而避免通过始终附加命名空间来使代码混乱。
回答by JeniT
There are a few techniques that you might try; which you use will depend on exactly what information you need to get out of the document, how rigorous you want to be, and how conformant the XPath implementation you're using is.
您可以尝试一些技巧;您使用的具体取决于您需要从文档中获取哪些信息、您希望达到的严格程度以及您使用的 XPath 实现的一致性。
One way to get the namespace URI associated with a particular prefix is using the namespace::axis. This will give you a namespace node whose name is the prefix and whose value is the namespace URI. For example, you could get the default namespace URI on the document element using the path:
获取与特定前缀关联的命名空间 URI 的一种方法是使用namespace::轴。这将为您提供一个名称空间节点,其名称是前缀,其值是名称空间 URI。例如,您可以使用以下路径获取文档元素上的默认命名空间 URI:
/*/namespace::*[name()='']
You might be able to use that to set up the namespace associations for your XPathNavigator. Be warned, though, that the namespace::axis is one of those corners of XPath 1.0 that isn't always implemented.
您可以使用它来为 XPathNavigator 设置命名空间关联。但是请注意,namespace::轴是 XPath 1.0 中并不总是实现的那些角落之一。
A second way of getting that namespace URI is to use the namespace-uri()function on the document element (which you've said will always be in that namespace). The expression:
获取该命名空间 URI 的第二种方法是namespace-uri()在文档元素上使用该函数(您已经说过它将始终在该命名空间中)。表达方式:
namespace-uri(/*)
will give you that namespace.
会给你那个命名空间。
An alternative would be to forget about associating a prefix with that namespace, and just make your path namespace-free. You can do this by using the local-name()function whenever you need to refer to an element whose namespace you don't know. For example:
另一种方法是忘记将前缀与该名称空间相关联,而让您的路径不受名称空间限制。local-name()每当您需要引用您不知道命名空间的元素时,您都可以通过使用该函数来完成此操作。例如:
//*[local-name() = 'Element']
You could go one step further and test the namespace URI of the element against the one of the document element, if you really wanted:
如果您真的想要,您可以更进一步,并根据文档元素之一测试元素的命名空间 URI:
//*[local-name() = 'Element' and namespace-uri() = namespace-uri(/*)]
A final option, given that the namespace seems to mean nothing to you, would be to run your XML through a filter that strips out the namespaces. Then you won't have to worry about them in your XPath at all. The easiest way to do that would be simply to remove the xmlnsattribute with a regular expression, but you could do something more complex if you needed to do other tidying at the same time.
考虑到命名空间对您来说似乎没有任何意义,最后一个选择是通过一个过滤器来运行您的 XML,该过滤器去除了命名空间。这样您就完全不必担心 XPath 中的它们了。最简单的方法是简单地xmlns使用正则表达式删除属性,但如果您需要同时进行其他整理,您可以做一些更复杂的事情。
回答by Dimitre Novatchev
This 40-line xslt transformation provides all the useful information about the namespaces in a given XML document:
这个 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>
When applied on the following XML document:
应用于以下 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>
the wanted result is produced:
产生了想要的结果:
<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>
回答by skaffman
Unfortunately, XPath doesn't have any concept of "default namespace". You need to register namespaces with prefixes with the XPath context, and then use those prefixes in your XPath expressions. It means for very verbose xpath, but it's a basic shortcoming of XPath 1. Apparently XPath 2 will address this, but that's no use to you right now.
不幸的是,XPath 没有任何“默认名称空间”的概念。您需要在 XPath 上下文中注册带有前缀的名称空间,然后在 XPath 表达式中使用这些前缀。这意味着非常冗长的 xpath,但它是 XPath 1 的一个基本缺点。显然 XPath 2 将解决这个问题,但现在对您没有用。
I suggest that you programmatically examine your XML document for the namespace, associate that namespace with a prefix in the XPath context, then use the prefix in the xpath expressions.
我建议您以编程方式检查名称空间的 XML 文档,将该名称空间与 XPath 上下文中的前缀相关联,然后在 xpath 表达式中使用该前缀。

