保留名称空间前缀定义的同时从XML文档中提取元素

时间:2019-07-03 14:58:33

标签: java xml

我正在尝试从XML文档中提取元素(作为字符串)。我已经尝试过建议使用in this SO answer的两种方法(也建议使用here的类似方法),但是它们都无法正确考虑可能在某些外部文档中定义的名称空间前缀。

使用以下代码:

// entry point method; see exampes of values for the String `s` in the question
public static String stripPayload(String s) throws Exception {
    final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
    final Document doc = dbf.newDocumentBuilder().parse(new InputSource(new StringReader(s)));

    final XPath xPath = XPathFactory.newInstance().newXPath();
    final String xPathToGetToTheNodeWeWishToExtract = "/*[local-name()='envelope']/*[local-name()='payload']";
    final Node result = (Node) xPath.evaluate(xPathToGetToTheNodeWeWishToExtract, doc, XPathConstants.NODE);
    return nodeToString_A(result); // or: nodeToString_B(result)

}

public static String nodeToString_A(Node node) throws Exception {
    final StringWriter buf = new StringWriter();
    final Transformer xform = TransformerFactory.newInstance().newTransformer();
    xform.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
    xform.setOutputProperty(OutputKeys.STANDALONE, "yes");
    xform.transform(new DOMSource(node), new StreamResult(buf));
    return(buf.toString());
}

public static String nodeToString_B(Node node) throws Exception {
    final Document document = node.getOwnerDocument();
    final DOMImplementationLS domImplLS = (DOMImplementationLS) document.getImplementation();
    final LSSerializer serializer = domImplLS.createLSSerializer();
    final String str = serializer.writeToString(node);
    return str;
}        

如果stripPayload方法传递了以下字符串:

<envelope><payload><a></a><b></b></payload></envelope>

<envelope><p:payload xmlns:p='foo'><a></a><b></b></p:payload></envelope>

nodeToString_AnodeToString_B方法均有效。但是,如果我传递以下同样有效的XML文档,其中在外部元素中定义了名称空间前缀:

<envelope xmlns:p='foo'><p:payload><a></a><b></b></p:payload></envelope>

…然后这两种方法都会失败,因为它们只是发出:

<p:payload><a/><b/></p:payload>

因此,由于忽略了名称空间前缀定义,他们已经在生成无效的文档。

下面更复杂的示例(在属性中使用名称空间前缀):

<envelope xmlns:p='foo' xmlns:a='alpha'><p:payload a:attr='dummy'><a></a><b></b></p:payload></envelope>

…实际上导致nodeToString_A失败,但有异常,而至少nodeToString_B产生了无效的结果:

<p:payload a:attr="dummy"><a/><b/></p:payload>

(同样,前缀没有定义)。

所以我的问题是:

以一种可以处理某些外部元素中定义的名称空间前缀的方式来提取内部XML元素并将其字符串化的健壮方法是什么?

1 个答案:

答案 0 :(得分:1)

您只需要启用 name-space-awareness

public static String stripPayload(String s) throws Exception {
    final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
    dbf.setNamespaceAware(true);

    ...
}

输出将是...

<p:payload xmlns:p="foo"><a/><b/></p:payload>