使用XPath从具有不必要命名空间的文档中提取XOM元素

时间:2012-03-12 19:33:38

标签: xpath xml-namespaces xom

我正在尝试使用XOM解析外部系统返回的一些HTML。 HTML看起来像这样:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<body>
  <div>
    Help I am trapped in a fortune cookie factory
  </div>
</body>
</html>

(实际上它非常麻烦,但它有这个DOCTYPE声明和这些命名空间和语言声明,并且上面的HTML表现出与真实HTML相同的问题。)

我想要做的是提取<div>的内容,但命名空间声明似乎让XPath感到困惑。如果我从文件中手工删除命名空间声明,则以下代码找到<div>,没问题:

Document document = ...
Nodes divs = document.query("//div");

但是使用命名空间,返回的Nodes的大小为0。

好吧,如果我以编程方式剥离命名空间呢?

Element rootElement = document.getRootElement();
rootElement.removeNamespaceDeclaration(rootElement.getNamespacePrefix());

......看起来应该可行,但什么也不做。来自javadoc

  

此方法仅删除添加了addNamespaceDeclaration.

的其他命名空间

好的,我想,我将为查询提供名称空间:

XPathContext context = 
    XPathContext.makeNamespaceContext(document.getRootElement());
Nodes divs = document.query("//div", context);

尺寸仍为零。

如何手动构建命名空间上下文?

XPathContext context = context = new XPathContext(
     rootElement.getNamespacePrefix(), rootElement.getNamespaceURI());
Nodes divs = document.query("//div", context);

XPathContext构造函数爆炸:

nu.xom.NamespaceConflictException: 
    XPath expressions do not use the default namespace

所以,我正在寻找:

  1. 使此查询有效的方法,或
  2. 一种以编程方式剥离名称空间声明的方法,或
  3. 对正确方法的解释,假设这些都是错误的。

  4. 更新:根据Lev Levitsky's answerJaxen FAQ,我提出了以下黑客行为:

    XPathContext context = new XPathContext(
        "foo", 
        document.getRootElement().getNamespaceURI());
    Nodes divs = document.query("//foo:div");
    

    这对我来说似乎有点疯狂,但我想这就是Jaxen希望你做事的方式。


    更新#2:如下所述和all over the Internet,这不是Jaxen的错;它只是XPath是XPath。

    所以,虽然这个hack有效,但我仍然想要一种去除命名空间声明的方法。最好不要走XSLT。

2 个答案:

答案 0 :(得分:2)

你可以写:

Nodes divs = document.query("//*[local-name()='div' and namespace-uri()='http://www.w3.org/1999/xhtml']");

答案 1 :(得分:1)

您应该直接使用

之类的内容指定命名空间
Nodes divs = document.query("//{http://www.w3.org/1999/xhtml}div");

或使用映射到各个名称空间的前缀(我猜这是NamespaceContext的用途,但查询中没有前缀。)

不幸的是,我不知道它是如何在Java中实现的,但如果有帮助我可以提供一个Python示例。