【问题标题】:Handling change in newlines by XML transformation for CDATA from Java 8 to Java 11通过从 Java 8 到 Java 11 的 CDATA 的 XML 转换处理换行符的变化
【发布时间】:2019-09-15 02:50:30
【问题描述】:

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

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

但在 Java 9 中,结果相同

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

这不是同一个 XML。

我了解到(来自不再可用的来源)对于 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 提交了一份错误报告,这已在 Java 14 中得到修复:https://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8223291

【问题讨论】:

  • 您应该编辑您的问题并添加一个演示问题的示例 Java 代码(生成一个小的 XML + 转换)。从一个工作示例开始会容易得多。

标签: java xml transformation sax java-11


【解决方案1】:

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

  • 你想要像这样的缩进:

    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 部分,并使用父元素删除空白兄弟。

【讨论】:

  • 谢谢!这实际上是一个非常干净的解决方法。我想知道我的代码依赖于未指定的行为是什么意思?
  • 你说转换应该做一个漂亮的打印;缩进每个元素。但是最新的 java 版本确实做到了:缩进 CDATA 部分。因此,CDATA 具有早期例外的味道。在每一种情况下,都找不到规范的错误。
  • 好吧,CDATA 后面可以跟“正常”数据。例如,这是有效的:foo。通过添加额外的空格,XML 的内容会发生变化。所以我确实认为这是 Transformer 的问题。
  • 那为什么 INDENT=yes?可以在 DTD/XSD 中限制允许的内容,但我认为这在这里不起作用(或一般的验证)。如果您之后在 DOM 中阅读,INDENT="no" 会不够。
  • Java 14 中已经修复了 CDATA 的问题。我在 ea 版本中进行了测试:openjdk version "14-ea" 2020-03-17 OpenJDK Runtime Environment (build 14-ea+6- 171)
猜你喜欢
  • 2019-04-07
  • 2021-09-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多