温馨提示:本文翻译自stackoverflow.com,查看原文请点击:java - extract an element out of an XML document while preserving namespace prefix definitions
java xml

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

发布于 2020-03-27 11:38:02

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

使用以下代码:

// 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元素并对其进行字符串化,从而处理可能在某些外部元素中定义的名称空间前缀?

查看更多

查看更多

提问者
Marcus Junius Brutus
被浏览
188
minus 2019-07-04 00:01

您只需要启用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>