我想从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_A
和nodeToString_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元素并对其进行字符串化,从而处理可能在某些外部元素中定义的名称空间前缀?
您只需要启用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>