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

Ric*_*ick 11 java xml sax transformation java-11

使用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,有一种解决方法可以使用DocumentBuilderFactorywith,setIgnoringElementContentWhitespace=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
}
Run Code Online (Sandbox Code Playgroud)

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

Joo*_*gen 5

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

  • 你想要的缩进如下:

      tform.setOutputProperty(OutputKeys.INDENT, "yes");
      tform.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
    
    Run Code Online (Sandbox Code Playgroud)
  • 但不适用于包含 CDATA 标记的元素:

      String xml = result.getWriter().toString();
      // No indentation (whitespace) for elements with a CDATA section.
      xml = xml.replaceAll("(?s)>\\s*(<\\!\\[CDATA\\[.*?]]>)\\s*</", ">$1</");
    
    Run Code Online (Sandbox Code Playgroud)

正则表达式使用:

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

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

  • CDATA 的问题已在 Java 14 中修复。我在 ea 版本中进行了测试:openjdk version "14-ea" 2020-03-17 OpenJDK Runtime Environment (build 14-ea+6-171) (3认同)