【发布时间】:2012-04-29 20:43:25
【问题描述】:
作为 Java 6 应用程序的一部分,我想查找 XML 文档中的所有命名空间声明,包括所有重复项。
编辑:根据 Martin 的要求,这是我正在使用的 Java 代码:
XPathFactory xPathFactory = XPathFactory.newInstance();
XPath xPath = xPathFactory.newXPath();
XPathExpression xPathExpression = xPathExpression = xPath.compile("//namespace::*");
NodeList nodeList = (NodeList) xPathExpression.evaluate(xmlDomDocument, XPathConstants.NODESET);
假设我有这个 XML 文档:
<?xml version="1.0" encoding="UTF-8"?>
<root xmlns:ele="element.com" xmlns:att="attribute.com" xmlns:txt="textnode.com">
<ele:one>a</ele:one>
<two att:c="d">e</two>
<three>txt:f</three>
</root>
为了查找所有命名空间声明,我将这个 xPath 语句应用于 XML 文档使用 xPath 1.0:
//namespace::*
它找到了 4 个命名空间声明,这是我所期望(和希望)的:
/root[1]/@xmlns:att - attribute.com
/root[1]/@xmlns:ele - element.com
/root[1]/@xmlns:txt - textnode.com
/root[1]/@xmlns:xml - http://www.w3.org/XML/1998/namespace
但是如果我改为使用 xPath 2.0,那么我会得到 16 个命名空间声明(之前的每个声明 4 次),这不是我所期望的(或期望的):
/root[1]/@xmlns:xml - http://www.w3.org/XML/1998/namespace
/root[1]/@xmlns:att - attribute.com
/root[1]/@xmlns:ele - element.com
/root[1]/@xmlns:txt - textnode.com
/root[1]/@xmlns:xml - http://www.w3.org/XML/1998/namespace
/root[1]/@xmlns:att - attribute.com
/root[1]/@xmlns:ele - element.com
/root[1]/@xmlns:txt - textnode.com
/root[1]/@xmlns:xml - http://www.w3.org/XML/1998/namespace
/root[1]/@xmlns:att - attribute.com
/root[1]/@xmlns:ele - element.com
/root[1]/@xmlns:txt - textnode.com
/root[1]/@xmlns:xml - http://www.w3.org/XML/1998/namespace
/root[1]/@xmlns:att - attribute.com
/root[1]/@xmlns:ele - element.com
/root[1]/@xmlns:txt - textnode.com
即使我使用 xPath 语句的非缩写版本,也会看到同样的差异:
/descendant-or-self::node()/namespace::*
在 oXygen 中测试过的各种 XML 解析器(LIBXML、MSXML.NET、Saxon)都可以看到它。 (编辑:正如我稍后在 cmets 中提到的,这种说法是不正确的。虽然我认为我正在测试各种 XML 解析器,但我真的没有。)
问题 #1:为什么从 xPath 1.0 到 xPath 2.0 的差异?
问题 #2: 使用 xPath 2.0 是否可能/合理地获得预期结果?
提示:在 xPath 2.0 中使用 distinct-values() 函数将不返回所需的结果,因为我想要所有命名空间声明,即使同一个命名空间被声明了两次。例如,考虑这个 XML 文档:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<bar:one xmlns:bar="http://www.bar.com">alpha</bar:one>
<bar:two xmlns:bar="http://www.bar.com">bravo</bar:two>
</root>
想要的结果是:
/root[1]/@xmlns:xml - http://www.w3.org/XML/1998/namespace
/root[1]/bar:one[1]/@xmlns:bar - http://www.bar.com
/root[1]/bar:two[1]/@xmlns:bar - http://www.bar.com
【问题讨论】:
-
James,请向我们展示查找命名空间“声明”的代码。在我的理解中,XPath
//namespace::*查找与命名空间声明不同的所有命名空间节点,因为命名空间节点存在于每个元素节点并且不在节点之间共享。因此,对于具有四个元素节点且根元素上有三个命名空间声明的 XML 文档,路径应该为四个元素中的每一个找到四个命名空间节点。据我所知,这在 XPath 1.0 和 2.0 之间应该是相同的。此外,/root[1]/@xmlns:txt之类的符号也具有误导性。 -
/root[1]/@xmlns:txt 表示法来自 oXygen。这是他们在节点列表中的节点表示,这很好。
-
上面添加的Java代码。很标准的东西。感谢您的解释。
-
我认为一个问题是您使用的 Java API 在 DOM 节点模型上工作,或者更确切地说将 XPath/XSLT 数据模型映射到 DOM 模型。 DOM 模型只有属性节点,其中一些是命名空间声明属性。 XSLT/XPath 模型具有属性节点和命名空间节点,命名空间声明不是该模型中的属性节点,例如
<foo xmlns:ns1="http://example.com/ns1"/>和foo元素在 XPath/XSLT 数据模型中没有属性节点,但有两个范围命名空间节点(一个在标记中,一个在 xml 命名空间中)。 -
继续我的评论:问题是您使用 XPath
//namespace::*选择了一些命名空间节点,然后使用 API 将结果呈现为 DOM 节点。该映射可能取决于实现。将 XPath 映射到 DOM 时还有其他已知问题,例如对于<foo><![CDATA[text 1]]>text2</foo>,当映射到DOM 时/foo/text()[1]选择的内容取决于实现,因为在DOM 中foo元素有两个子节点,一个CDATA 节节点和一个文本节点,而XPath 模型只有一个文本节点。跨度>
标签: java xml xpath xml-namespaces saxon