通过CDATA从Java 8到Java 11的XML转换处理换行符

问题描述 投票:10回答:1

在Java 9中,javax.xml.transform.TransformerOutputKeys.INDENT处理CDATA标签的方式发生了变化。简而言之,在Java 8中,包含一些字符数据的名为“test”的标记将导致:

<test><![CDATA[data]]></test>

但是使用Java 9会产生相同的结果

<test> <![CDATA[data]]> </test>

哪个是不一样的XML。有关更多信息,请参阅http://java9.wtf/xml-transformer/

据我所知,对于Java 9,有一个使用DocumentBuilderFactorysetIgnoringElementContentWhitespace=true的解决方法,但这不再适用于Java 11。

有没有人知道在Java 11中处理这个问题的方法?我要么想要一种方法来阻止额外的换行(但仍然能够格式化我的XML),或者在解析XML时能够忽略它们(最好使用SAX)。

不幸的是,我不知道CDATA标签在我的应用程序中实际包含的内容。它可能以空格或换行符开头或结尾,因此我不能在读取XML时实际删除它们或实际设置结果对象中的值。

演示问题的示例程序:

public static void main(String[] args) throws TransformerException, ParserConfigurationException, IOException, SAXException
{
    String data = "data";

    StreamSource source = new StreamSource(new StringReader("<foo><bar><![CDATA[" + data + "]]></bar></foo>"));
    StreamResult result = new StreamResult(new StringWriter());

    Transformer tform = TransformerFactory.newInstance().newTransformer();
    tform.setOutputProperty(OutputKeys.INDENT, "yes");
    tform.transform(source, result);

    String xml = result.getWriter().toString();

    System.out.println(xml); // I expect bar and CDATA to be on same line. This is true for Java 8, false for Java 11


    Document document = DocumentBuilderFactory.newInstance()
        .newDocumentBuilder()
        .parse(new InputSource(new StringReader(xml)));

    String resultData = document.getElementsByTagName("bar")
        .item(0)
        .getTextContent();

    System.out.println(data.equals(resultData)); // True for Java 8, false for Java 11
}

编辑:为了将来的参考,我已经向Oracle提交了一个错误报告:https://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8223291

java xml transformation sax java-11
1个回答
2
投票

由于您的代码依赖于未指定的行为,因此额外的显式代码似乎更好:

  • 你想要缩进像: tform.setOutputProperty(OutputKeys.INDENT, "yes"); tform.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
  • 但不适用于包含CDATA的元素。 String xml = result.getWriter().toString(); // No indentation (whitespace) for elements with a CDATA section. xml = xml.replaceAll(">\\s*(<\\!\\[CDATA\\[.*?]]>)\\s*</", ">$1</");

正则表达式使用:

  • (?s) DOT_ALL让.匹配任何角色,也是换行符。
  • .*?最短的匹配序列,不匹配“...]]> ...]]>”。

或者:在DOM树(保留CDATA)中,您可以检索每个XPath的所有CDATA部分,并使用父元素删除空白兄弟。

© www.soinside.com 2019 - 2024. All rights reserved.