如何使用XPath中的Java命名空间查询XML?

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (37)

当我的XML看起来像这样(否xmlns),那么我可以轻松地用XPath查询它/workbook/sheets/sheet[1]

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<workbook>
  <sheets>
    <sheet name="Sheet1" sheetId="1" r:id="rId1"/>
  </sheets>
</workbook>

但是,当它看起来像这样,然后我不能

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
  <sheets>
    <sheet name="Sheet1" sheetId="1" r:id="rId1"/>
  </sheets>
</workbook>

有任何想法吗?

提问于
用户回答回答于

在第二个示例XML文件中,元素绑定到名称空间。您的XPath尝试处理绑定到默认“no namespace”命名空间的元素,因此它们不匹配。

首选方法是使用名称空间前缀注册名称空间。它使您的XPath更易于开发,读取和维护。

但是,注册名称空间并在您的XPath中使用名称空间前缀并不是强制性的。

可以制定一个XPath表达式,该表达式对元素和谓词过滤器使用通用匹配,以限制匹配所需的local-name()namespace-uri()。例如:

/*[local-name()='workbook'
    and namespace-uri()='http://schemas.openxmlformats.org/spreadsheetml/2006/main']
  /*[local-name()='sheets'
      and namespace-uri()='http://schemas.openxmlformats.org/spreadsheetml/2006/main']
  /*[local-name()='sheet'
      and namespace-uri()='http://schemas.openxmlformats.org/spreadsheetml/2006/main'][1]

正如你所看到的,它会产生一个非常长而且冗长的XPath语句,这个语句很难读取(和维护)。

您也可以匹配local-name()元素并忽略名称空间。例如:

/*[local-name()='workbook']/*[local-name()='sheets']/*[local-name()='sheet'][1]

但是,冒着匹配错误元素的风险。如果您的XML混合使用相同的词汇表(这可能不是此实例的问题),那么local-name()您的XPath可以匹配错误的元素并选择错误的内容:

用户回答回答于

你的问题是默认的命名空间。查看这篇文章,了解如何处理XPath中的命名空间:http : //www.edankert.com/defaultnamespaces.html

他们得出的结论之一是:

因此,为了能够在(默认)名称空间中定义的XML内容上使用XPath表达式,我们需要指定一个名称空间前缀映射

请注意,这并不意味着您必须以任何方式更改您的源文档(尽管如果您愿意,可以将名称空间前缀放在那里)。听起来很奇怪,对吧?什么,你做的是在你的Java代码和使用创建一个命名空间前缀映射说在你的XPath表达式的前缀。在这里,我们将创建一个spreadsheet到默认命名空间的映射。

XPathFactory factory = XPathFactory.newInstance();
XPath xpath = factory.newXPath();

// there's no default implementation for NamespaceContext...seems kind of silly, no?
xpath.setNamespaceContext(new NamespaceContext() {
    public String getNamespaceURI(String prefix) {
        if (prefix == null) throw new NullPointerException("Null prefix");
        else if ("spreadsheet".equals(prefix)) return "http://schemas.openxmlformats.org/spreadsheetml/2006/main";
        else if ("xml".equals(prefix)) return XMLConstants.XML_NS_URI;
        return XMLConstants.NULL_NS_URI;
    }

    // This method isn't necessary for XPath processing.
    public String getPrefix(String uri) {
        throw new UnsupportedOperationException();
    }

    // This method isn't necessary for XPath processing either.
    public Iterator getPrefixes(String uri) {
        throw new UnsupportedOperationException();
    }
});

// note that all the elements in the expression are prefixed with our namespace mapping!
XPathExpression expr = xpath.compile("/spreadsheet:workbook/spreadsheet:sheets/spreadsheet:sheet[1]");

// assuming you've got your XML document in a variable named doc...
Node result = (Node) expr.evaluate(doc, XPathConstants.NODE);

瞧...现在你已经将元素保存在result变量中了。

警告:如果您使用标准JAXP类将您的XML解析为DOM,请务必setNamespaceAware(true)使用您的DocumentBuilderFactory。否则,这段代码将不起作用!

扫码关注云+社区