如何在Java中从xml节点创建格式化的字符串

时间:2018-07-24 04:58:02

标签: java xml dom

我正在尝试从XML节点创建格式化的字符串。参见以下示例:

<?xml version="1.0" encoding="UTF-8"?>
<root>
    <parent>
        <foo>
            <bar>foo</bar>
        </foo>        
    </parent>
</root>

我要为其创建格式字符串的节点是“ foo”。我期望这样的结果:

<foo>
  <bar>foo</bar>
</foo>

但是实际结果是:

<foo>
            <bar>foo</bar>
        </foo>

我的方法如下:

public String toXmlString(Node node) throws TransformerException {
    final Transformer transformer = TransformerFactory.newInstance().newTransformer();
    transformer.setOutputProperty(OutputKeys.METHOD, "xml");
    transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
        transformer.setOutputProperty(OutputKeys.INDENT, "yes");     

    final Writer writer = new StringWriter();
    final StreamResult streamResult = new StreamResult(writer);

    transformer.transform(new DOMSource(node), streamResult);
    return writer.toString();
}

我在做什么错了?

4 个答案:

答案 0 :(得分:1)

它确实在做应该做的事情。 indent="yes"允许转换为 add 空格以缩进元素,但不能删除空格,因为它不知道输入中哪个空格很重要。

在您提供的输入中,<foo></foo>元素行有8个前导空格,而<bar>行有12个。

<foo>开头标记没有缩进的原因是,前面的空格实际上属于包含的<parent>元素,并且在传递给转换的子树中不存在。

标准(XSLT 1XSLT 2)中详细介绍了空格剥离行为。总结

  

如果满足以下任一条件,则保留空白文本节点:

     
      
  • 文本节点父级的元素名称在保留空白的元素名称集中
  •   
  • ...
  •   

  

(XSLT 2)保留空格的元素名称集由xsl:strip-space和xsl:preserve-space声明指定。元素名称是否包含在保留空白的名称集中,取决于所有xsl:strip-space或xsl:preserve-space声明中的最佳匹配:只有并且不存在匹配或最佳匹配时,才包含该元素名称match是一个xsl:preserve-space元素。

在XSLT 1规范中更简单地陈述:

  

最初,保留空白的元素名称集包含所有元素名称。

不幸的是,使用xsl:strip-space不会产生您想要的结果。使用<xsl:strip-space elements="*">(和indent="yes"),我得到以下输出:

<foo><bar>foo</bar>
</foo>

有道理。去除空格,然后使</foo>标签在其开始标签下对齐。

答案 1 :(得分:0)

这将与第三方库JDOM 2更好地配合使用,这也使处理DOM文档的一切变得更容易。

它的“漂亮格式”输出将按预期缩进,从而删除现有的缩进,只要删除/更改的文本节点仅是空格。如果要保留空白,则不需要缩进。

看起来像这样:

public String toXmlString(Element element) {
  return new XMLOutputter(Format.getPrettyFormat()).outputString(element);
}

答案 2 :(得分:0)

只要您在输入中去除空格,Saxon就会提供所需的输出:

    public void testIndentation() {
        try {
            String in = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
                    + "<root>\n"
                    + "    <parent>\n"
                    + "        <foo>\n"
                    + "            <bar>foo</bar>\n"
                    + "        </foo>        \n"
                    + "    </parent>\n"
                    + "</root>";
            Processor proc = new Processor(false);
            DocumentBuilder builder = proc.newDocumentBuilder();
            builder.setWhitespaceStrippingPolicy(WhitespaceStrippingPolicy.ALL); //XX
            XdmNode doc = builder.build(new StreamSource(new StringReader(in)));
            StringWriter sw = new StringWriter();
            Serializer serializer = proc.newSerializer(sw);
            serializer.setOutputProperty(Serializer.Property.METHOD, "xml");
            serializer.setOutputProperty(Serializer.Property.INDENT, "yes");
            XdmNode foo = doc.axisIterator(Axis.DESCENDANT, new QName("foo")).next();
            serializer.serializeNode(foo);
            System.err.println(sw);
        } catch (SaxonApiException err) {
            fail();
        }
    }

但是,如果不删除空格(在第XX行注释),则会在文章中看到输出参差不齐的内容。从XSLT 2.0起,该规范允许处理器比这更智能,但是Saxon没有利用这一优势。一个原因是序列化是完全流式传输的:它孤立地查看每个事件(开始元素,结束元素等),而不是整个文档。

答案 3 :(得分:0)

基于kumesana的答案,我找到了可接受的解决方案:

public String toXmlString(Node node) throws TransformerException {
    final DOMBuilder builder = new DOMBuilder();
    final Element element = (Element) node;
    final org.jdom2.Element jdomElement = builder.build(element);

    final XMLOutputter xmlOutputter = new XMLOutputter(Format.getPrettyFormat());
    final String output = xmlOutputter.outputString(jdomElement);
    return output;
}