在Java DOM中将节点的内部XML作为String获取

我有一个XML org.w3c.dom.Node,如下所示:

 
foo bar

如何将
foo bar
部分作为字符串?

同样的问题。 为了解决这个问题,我编写了这个辅助函数:

 public String innerXml(Node node) { DOMImplementationLS lsImpl = (DOMImplementationLS)node.getOwnerDocument().getImplementation().getFeature("LS", "3.0"); LSSerializer lsSerializer = lsImpl.createLSSerializer(); NodeList childNodes = node.getChildNodes(); StringBuilder sb = new StringBuilder(); for (int i = 0; i < childNodes.getLength(); i++) { sb.append(lsSerializer.writeToString(childNodes.item(i))); } return sb.toString(); } 

对于org.w3c.dom.Node ,没有简单的方法。 getTextContent()给出连接在一起的每个子节点的文本。 getNodeValue()将为您提供当前节点的文本(如果它是AttributeCDATAText节点)。 因此,您需要使用getChildNodes()getNodeName()getNodeValue()的组合来序列化节点以构建字符串。

您也可以使用存在的各种XML序列化库之一来完成此操作。 有XStream甚至是JAXB。 这在这里讨论: Java中的XML序列化?

如果你正在使用jOOX ,你可以用类似jquery的语法包装你的节点,并在其上调用toString()

 $(node).toString(); 

它在内部使用身份变换器,如下所示:

 ByteArrayOutputStream out = new ByteArrayOutputStream(); Transformer transformer = TransformerFactory.newInstance().newTransformer(); transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); Source source = new DOMSource(element); Result target = new StreamResult(out); transformer.transform(source, target); return out.toString(); 

根据Andrey M的回答,我不得不稍微修改代码以获得完整的DOM文档。 如果你只是使用

  NodeList childNodes = node.getChildNodes(); 

它没有包括我的根元素。 要包含root元素(并获取完整的.xml文档),我使用了:

  public String innerXml(Node node) { DOMImplementationLS lsImpl = (DOMImplementationLS)node.getOwnerDocument().getImplementation().getFeature("LS", "3.0"); LSSerializer lsSerializer = lsImpl.createLSSerializer(); lsSerializer.getDomConfig().setParameter("xml-declaration", false); StringBuilder sb = new StringBuilder(); sb.append(lsSerializer.writeToString(node)); return sb.toString(); } 

如果您不想使用外部库,以下解决方案可能会派上用场。 如果您有一个节点并且您想要提取父元素的子元素,请按以下步骤操作:

  StringBuilder resultBuilder = new StringBuilder(); // Get all children of the given parent node NodeList children = parent.getChildNodes(); try { // Set up the output transformer TransformerFactory transfac = TransformerFactory.newInstance(); Transformer trans = transfac.newTransformer(); trans.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); trans.setOutputProperty(OutputKeys.INDENT, "yes"); StringWriter stringWriter = new StringWriter(); StreamResult streamResult = new StreamResult(stringWriter); for (int index = 0; index < children.getLength(); index++) { Node child = children.item(index); // Print the DOM node DOMSource source = new DOMSource(child); trans.transform(source, streamResult); // Append child to end result resultBuilder.append(stringWriter.toString()); } } catch (TransformerException e) { //Error handling goes here } return resultBuilder.toString(); 

我遇到了问题,最后一个答案是’nodeToStream()’方法是未定义的; 因此,我的版本在这里:

  public static String toString(Node node){ String xmlString = ""; try { Transformer transformer = TransformerFactory.newInstance().newTransformer(); transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); //transformer.setOutputProperty(OutputKeys.INDENT, "yes"); Source source = new DOMSource(node); StringWriter sw = new StringWriter(); StreamResult result = new StreamResult(sw); transformer.transform(source, result); xmlString = sw.toString (); } catch (Exception ex) { ex.printStackTrace (); } return xmlString; } 

到目前为止,安德烈M的最佳解决方案需要一个特定的实现,这可能会在未来引发问题。 这是相同的方法,但只有JDK允许您进行序列化(即,配置为使用的内容)。

 public static String innerXml(Node node) throws Exception { StringWriter writer = new StringWriter(); Transformer transformer = TransformerFactory.newInstance().newTransformer(); transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); NodeList childNodes = node.getFirstChild().getChildNodes(); for (int i = 0; i < childNodes.getLength(); i++) { transformer.transform(new DOMSource(childNodes.item(i)), new StreamResult(writer)); } return writer.toString(); } 

如果您正在处理文档而不是节点,则必须深入一级并使用node.getFirstChild().getChildNodes(); 但是,为了使其更加健壮,您应该找到第一个元素,而不仅仅是理所当然地认为只有一个节点。 XML必须具有单个根元素,但可以包含多个节点,包括注释,实体和空白文本。

  Node rootElement = docRootNode.getFirstChild(); while (rootElement != null && rootElement.getNodeType() != Node.ELEMENT_NODE) rootElement = rootElement.getNextSibling(); if (rootElement == null) throw new RuntimeException("No root element found in given document node."); NodeList childNodes = rootElement.getChildNodes(); 

如果我应该推荐一个库来处理它,请尝试JSoup,它主要用于HTML,但也适用于XML 。 我没有测试过。

 Document doc = Jsoup.parse(xml, "", Parser.xmlParser()); fileContents.put(Attributes.BODY, document.body().html()); // versus: document.body().outerHtml() 

我想延伸来自Andrey M的非常好的答案:

可能会发生节点不可序列化,这会导致某些实现出现以下exception:

 org.w3c.dom.ls.LSException: unable-to-serialize-node: unable-to-serialize-node: The node could not be serialized. 

我在Wildfly 13上运行的实现“ org.apache.xml.serialize.DOMSerializerImpl.writeToString(DOMSerializerImpl) ”遇到了这个问题。

为了解决这个问题,我建议稍微更改Andrey M.的代码示例:

 private static String innerXml(Node node) { DOMImplementationLS lsImpl = (DOMImplementationLS) node.getOwnerDocument().getImplementation().getFeature("LS", "3.0"); LSSerializer lsSerializer = lsImpl.createLSSerializer(); lsSerializer.getDomConfig().setParameter("xml-declaration", false); NodeList childNodes = node.getChildNodes(); StringBuilder sb = new StringBuilder(); for (int i = 0; i < childNodes.getLength(); i++) { Node innerNode = childNodes.item(i); if (innerNode!=null) { if (innerNode.hasChildNodes()) { sb.append(lsSerializer.writeToString(innerNode)); } else { sb.append(innerNode.getNodeValue()); } } } return sb.toString(); } 

我还添加了Nyerguds的评论。 这适用于我在wildfly 13。

这是一个提取org.w3c.dom.Node内容的替代解决方案。 如果节点内容不包含xml标记,则此解决方案也适用:

 private static String innerXml(Node node) throws TransformerFactoryConfigurationError, TransformerException { StringWriter writer = new StringWriter(); String xml = null; Transformer transformer = TransformerFactory.newInstance().newTransformer(); transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); transformer.transform(new DOMSource(node), new StreamResult(writer)); // now remove the outer tag.... xml = writer.toString(); xml = xml.substring(xml.indexOf(">") + 1, xml.lastIndexOf(" 

在Lukas Eder的解决方案之上,我们可以像.NET一样提取innerXml,如下所示

  public static String innerXml(Node node,String tag){ String xmlstring = toString(node); xmlstring = xmlstring.replaceFirst("<[/]?"+tag+">",""); return xmlstring; } public static String toString(Node node){ String xmlString = ""; Transformer transformer; try { transformer = TransformerFactory.newInstance().newTransformer(); transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); //transformer.setOutputProperty(OutputKeys.INDENT, "yes"); StreamResult result = new StreamResult(new StringWriter()); xmlString = nodeToStream(node, transformer, result); } catch (TransformerConfigurationException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (TransformerFactoryConfigurationError e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (TransformerException e) { // TODO Auto-generated catch block e.printStackTrace(); }catch (Exception ex){ ex.printStackTrace(); } return xmlString; } 

例如:

 If Node name points to xml with string representation "ChristianBale" String innerXml = innerXml(name,"Name"); //returns "ChristianBale"