什么XPath表达式找到具有给定命名空间声明的元素集?

时间:2012-02-28 12:24:52

标签: java xml xpath xml-namespaces

假设我有一个带有2个名称空间声明的XML文档,其前缀为foo,如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<root xmlns:foo="http://www.foo.com">
  <one>
    <!-- children nodes here -->
  </one>
  <two>
    <!-- children nodes here -->
  </two>
  <three xmlns:foo="http://www.foo.com">
    <!-- children nodes here -->
  </three>
</root>

我想评估一个XPath表达式(在Java中),该表达式将返回具有此命名空间声明的元素的NodeList,即rootthree节点。我不是在寻找这个命名空间在范围内的所有节点,而只是寻找具有命名空间声明的节点。

这是我打算使用的Java:

XPathFactory xPathFactory = XPathFactory.newInstance();
XPath xPath = xPathFactory.newXPath();
XPathExpression xPathExpression = null;  
NodeList nodeList = null;
boolean theExpressionWasCompiled = true;
xPathExpression = xPath.compile(xPathStatement); // XPath goes here!
nodeList = (NodeList) xPathExpression.evaluate(document, XPathConstants.NODESET);

我应该使用哪种XPath(xPathStatement方法的compile()值)?

编辑:XPath 1或2 ok。

最终编辑:事实证明,XPath不能完全符合我的要求(如果你想要细节,请参阅下面的Dimitre的解释)。我能做的最好的事情是多次评估XPath(每个命名空间声明一次),以找到具有命名空间声明的每个元素。我碰巧已经知道每个命名空间的声明次数,因此知道要评估多少次对我来说不是问题。不是超级高效,但确实有效。这是我使用的XPath,它与Dimitre提出的非常类似(见下文):

//*[namespace::*[local-name() = 'foo']]
     [not
       (parent::node()
         [namespace::*
           [local-name() = 'foo']
         ]
       )
     ]

感谢我的朋友Roger Costello制作我使用过的XPath。

2 个答案:

答案 0 :(得分:5)

根据我的理解,XPath无法满足您的需求。 XPath数据模型具有名称空间节点,这些节点位于任何给定元素节点的范围内;在该模型中是否解析

<root xmlns:foo="http://example.com/">
  <child>
    <grandchild/>
  </child>
</root>

<root xmlns:foo="http://example.com/">
  <child xmlns:foo="http://example.com/">
    <grandchild/>
  </child>
</root>

<root xmlns:foo="http://example.com/">
  <child xmlns:foo="http://example.com/">
    <grandchild xmlns:foo="http://example.com/"/>
  </child>
</root>

在暴露于XPath(和XSLT或XQuery)的模型中没有区别,在所有三种情况下,所有三个元素节点都有一个命名空间节点,其范围为本地名称foo和值http://example.com/

基于此,我没有看到如何编写XPath来区分由于名称空间声明而在范围内具有命名空间节点的元素节点以及从祖先元素继承它的那些节点。

所以我不认为您的问题可以通过XPath解决。你可能想要等到Dimitre之类的人确认或拒绝我的观点。

答案 1 :(得分:1)

  

我想评估一个XPath表达式(在Java中)   返回具有此命名空间声明的元素的NodeList,   即rootthree个节点。我不是在寻找所有节点   此命名空间在范围内的位置,只有具有该命名空间的节点   命名空间声明。

此信息在解析期间丢失 - 未保存在XML Infoset中,该信息是由解析XML文档而创建的,并由XPath处理器使用。

因此,当元素具有命名空间节点(但它只是继承而不是重新声明)时,使用XPath来区分是不可能的,以及元素具有命名空间的情况注意,除此之外,它在元素上声明。

唯一的例外是该元素是其ancestor-or-self::*序列中具有此命名空间的第一个元素。在这种情况下,显然,命名空间节点继承,因此必须在元素上声明:

//*[namespace::*
      [name() = 'foo' and . = 'http://www.foo.com']
  and
    not(parent::*
         [namespace::*
           [name() = 'foo' and . = 'http://www.foo.com']
         ]
        )
    ]

在提供的XML文档上评估此XPath表达式

<root xmlns:foo="http://www.foo.com">
    <one>
        <!-- children nodes here -->
    </one>
    <two>
        <!-- children nodes here -->
    </two>
    <three xmlns:foo="http://www.foo.com">
        <!-- children nodes here -->
    </three>
</root>

选择名为root的元素 - 因为它应该