7

作为 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 1.0将此 xPath 语句应用于 XML 文档:

//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)中都可以看到它。(编辑:正如我稍后在评论中提到的,这种说法是不正确的。虽然我认为我正在测试各种 XML 解析器,但我真的不是。)

问题一: 为什么从 xPath 1.0 到 xPath 2.0 的区别?

问题2: 使用 xPath 2.0 获得预期结果是否可能/合理?

提示:使用distinct-values()xPath 2.0 中的函数不会返回所需的结果,因为我想要所有命名空间声明,即使同一个命名空间被声明了两次。例如,考虑这个 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
4

4 回答 4

8

我认为这将获得所有命名空间,没有任何重复:

for $i in 1 to count(//namespace::*) return 
if (empty(index-of((//namespace::*)[position() = (1 to ($i - 1))][name() = name((//namespace::*)[$i])], (//namespace::*)[$i]))) 
then (//namespace::*)[$i] 
else ()
于 2012-05-02T17:22:17.330 回答
4

为了查找所有命名空间声明,我使用 xPath 1.0 将此 xPath 语句应用于 XML 文档:

//namespace::* It finds 4 namespace declarations, which is what I expect (and desire):

/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 1.0 实现

使用我拥有的所有 XSLT 1.0 处理器,我得到了不同且正确的结果。此转换(仅评估 XPath 表达式并为每个选定的命名空间节点打印一行):

<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:template match="/">
     <xsl:for-each select="//namespace::*">
       <xsl:value-of select="concat(name(), ': ', ., '&#xA;')"/>
     </xsl:for-each>
 </xsl:template>
</xsl:stylesheet>

应用于提供的 XML 文档时:

<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>

产生正确的结果

xml: http://www.w3.org/XML/1998/namespace
ele: element.com
att: attribute.com
txt: textnode.com
xml: http://www.w3.org/XML/1998/namespace
ele: element.com
att: attribute.com
txt: textnode.com
xml: http://www.w3.org/XML/1998/namespace
ele: element.com
att: attribute.com
txt: textnode.com
xml: http://www.w3.org/XML/1998/namespace
ele: element.com
att: attribute.com
txt: textnode.com

使用所有这些 XSLT 1.0 和 XSLT 2.0 处理器

MSXML3、MSXML4、MSXML6、.NET XslCompiledTransform、.NET XslTransform、Altova (XML SPY)、Saxon 6.5.4、Saxon 9.1.07、XQSharp。

这是一个简短的 C# 程序,它确认在 .NET 中选择的节点数为 16:

namespace TestNamespaces
{
    using System;
    using System.IO;
    using System.Xml.XPath;

    class Test
    {
        static void Main(string[] args)
        {
            string xml =
@"<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>";
            XPathDocument doc = new XPathDocument(new StringReader(xml));

            double count = 
              (double) doc.CreateNavigator().Evaluate("count(//namespace::*)");

            Console.WriteLine(count);
        }
    }
}

结果是

16.

更新

这是一个 XPath 2.0 表达式,它只查找“不同的”命名空间节点并为每个节点生成一行名称 - 值对:

for $i in distinct-values(
             for $ns in //namespace::*
               return
                  index-of(
                           (for $x in //namespace::*
                             return
                                concat(name($x), ' ', string($x))

                            ),
                            concat(name($ns), ' ', string($ns))
                          )
                          [1]
                                                  )
  return
    for $x in (//namespace::*)[$i]
     return
        concat(name($x), ' :', string($x), '&#xA;')
于 2012-04-18T13:07:02.813 回答
3

正如前面的线程所指出的,//namespace::*将根据 XPath 1.0 和 XPath 2.0 实现返回所有命名空间节点,其中有 16 个。如果您发现没有正确实现规范的实现,我不会感到惊讶。

使用 XPath 1.0 或 XPath 2.0 查找所有命名空间声明(与命名空间节点不同)通常是不可能的,因为以下两个文档在数据模型级别被认为是等效的:

文件 A:

<a xmlns="one">
  <b/>
</a> 

文件 B:

<a xmlns="one">
  <b xmlns="one"/>
</a>

但是,如果我们将“重要命名空间声明”定义为存在于子元素上但不存在于其父元素上的命名空间,那么您可以尝试以下 XPath 2.0 表达式:

for $e in //* return
  for $n in $e/namespace::* return
     if (not(some $p in $n/../namespace::* satisfies ($p/name() eq $e/name() and string($p) eq string($n)))) then concat($e/name(), '->', $n/name(), '=', string($n)) else ()
于 2012-04-18T16:15:54.737 回答
0

这是我使用 .NET XPathDocument(XSLT/XPath 1.0 数据模型)、XmlDocument(DOM 数据模型)和 MSXML 6 的 DOM 的 XPath 1.0 实现的结果;针对您的示例 XML 文档运行的测试代码是

    Console.WriteLine("XPathDocument:");
    XPathDocument xpathDoc = new XPathDocument("../../XMLFile4.xml");
    foreach (XPathNavigator nav in xpathDoc.CreateNavigator().Select("//namespace::*"))
    {
        Console.WriteLine("Node type: {0}; name: {1}; value: {2}.", nav.NodeType, nav.Name, nav.Value);
    }
    Console.WriteLine();

    Console.WriteLine("DOM XmlDocument:");
    XmlDocument doc = new XmlDocument();
    doc.Load("../../XMLFile4.xml");
    foreach (XmlNode node in doc.SelectNodes("//namespace::*"))
    {
        Console.WriteLine("Node type: {0}; name: {1}; value: {2}.", node.NodeType, node.Name, node.Value);
    }
    Console.WriteLine();


    Console.WriteLine("MSXML 6 DOM:");
    dynamic msxmlDoc = Activator.CreateInstance(Type.GetTypeFromProgID("Msxml2.DOMDocument.6.0"));
    msxmlDoc.load("../../XMLFile4.xml");
    foreach (dynamic node in msxmlDoc.selectNodes("//namespace::*"))
    {
        Console.WriteLine("Node type: {0}; name: {1}; value: {2}.", node.nodeType, node.name, node.nodeValue);
    }

它的输出是

XPathDocument:
Node type: Namespace; name: txt; value: textnode.com.
Node type: Namespace; name: att; value: attribute.com.
Node type: Namespace; name: ele; value: element.com.
Node type: Namespace; name: xml; value: http://www.w3.org/XML/1998/namespace.
Node type: Namespace; name: txt; value: textnode.com.
Node type: Namespace; name: att; value: attribute.com.
Node type: Namespace; name: ele; value: element.com.
Node type: Namespace; name: xml; value: http://www.w3.org/XML/1998/namespace.
Node type: Namespace; name: txt; value: textnode.com.
Node type: Namespace; name: att; value: attribute.com.
Node type: Namespace; name: ele; value: element.com.
Node type: Namespace; name: xml; value: http://www.w3.org/XML/1998/namespace.
Node type: Namespace; name: txt; value: textnode.com.
Node type: Namespace; name: att; value: attribute.com.
Node type: Namespace; name: ele; value: element.com.
Node type: Namespace; name: xml; value: http://www.w3.org/XML/1998/namespace.

DOM XmlDocument:
Node type: Attribute; name: xmlns:txt; value: textnode.com.
Node type: Attribute; name: xmlns:att; value: attribute.com.
Node type: Attribute; name: xmlns:ele; value: element.com.
Node type: Attribute; name: xmlns:xml; value: http://www.w3.org/XML/1998/namespa
ce.
Node type: Attribute; name: xmlns:txt; value: textnode.com.
Node type: Attribute; name: xmlns:att; value: attribute.com.
Node type: Attribute; name: xmlns:ele; value: element.com.
Node type: Attribute; name: xmlns:xml; value: http://www.w3.org/XML/1998/namespa
ce.
Node type: Attribute; name: xmlns:txt; value: textnode.com.
Node type: Attribute; name: xmlns:att; value: attribute.com.
Node type: Attribute; name: xmlns:ele; value: element.com.
Node type: Attribute; name: xmlns:xml; value: http://www.w3.org/XML/1998/namespa
ce.
Node type: Attribute; name: xmlns:txt; value: textnode.com.
Node type: Attribute; name: xmlns:att; value: attribute.com.
Node type: Attribute; name: xmlns:ele; value: element.com.
Node type: Attribute; name: xmlns:xml; value: http://www.w3.org/XML/1998/namespa
ce.

MSXML 6 DOM:
Node type: 2; name: xmlns:xml; value: http://www.w3.org/XML/1998/namespace.
Node type: 2; name: xmlns:ele; value: element.com.
Node type: 2; name: xmlns:att; value: attribute.com.
Node type: 2; name: xmlns:txt; value: textnode.com.
Node type: 2; name: xmlns:xml; value: http://www.w3.org/XML/1998/namespace.
Node type: 2; name: xmlns:ele; value: element.com.
Node type: 2; name: xmlns:att; value: attribute.com.
Node type: 2; name: xmlns:txt; value: textnode.com.
Node type: 2; name: xmlns:xml; value: http://www.w3.org/XML/1998/namespace.
Node type: 2; name: xmlns:ele; value: element.com.
Node type: 2; name: xmlns:att; value: attribute.com.
Node type: 2; name: xmlns:txt; value: textnode.com.
Node type: 2; name: xmlns:xml; value: http://www.w3.org/XML/1998/namespace.
Node type: 2; name: xmlns:ele; value: element.com.
Node type: 2; name: xmlns:att; value: attribute.com.
Node type: 2; name: xmlns:txt; value: textnode.com.

所以这肯定不是 XPath 1.0 与 XPath 2.0 的问题。我认为您看到的问题是将具有命名空间节点的 XPath 数据模型映射到具有属性节点的 DOM 模型的缺点。更熟悉 Java XPath API 的人需要告诉您,您看到的行为是否正确地依赖于实现,因为 API 规范对于将 XPath 命名空间轴映射到 DOM 模型的情况不够精确,或者它是否是一个错误。

于 2012-04-18T16:02:01.873 回答