通过 XML 转换 CDATA 从 Java 8 到 Java 11 处理换行符的变化
Handling change in newlines by XML transformation for CDATA from Java 8 to Java 11
在 Java 9 中,javax.xml.transform.Transformer
with OutputKeys.INDENT
处理 CDATA 标签的方式发生了变化。简而言之,在 Java 8 中,包含一些字符数据的名为 'test' 的标签将导致:
<test><![CDATA[data]]></test>
但是 Java 9 的结果相同
<test>
<![CDATA[data]]>
</test>
不一样XML。
我了解到(来自不再可用的来源)对于 Java 9 有一个使用 DocumentBuilderFactory
with setIgnoringElementContentWhitespace=true
的解决方法,但这不再适用于 Java 11.
有人知道在 Java 11 中处理这个问题的方法吗?我正在寻找一种方法来防止额外的换行符(但仍然能够格式化我的 XML),或者在解析 XML 时能够忽略它们(最好使用 SAX)。
遗憾的是,我不知道 CDATA 标记在我的应用程序中实际包含什么。它可能以白色 space 或换行符开头或结尾,因此我不能在读取 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
由于您的代码依赖于未指定的行为,额外的显式代码似乎更好:
您希望缩进为:
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*</", "></");
正则表达式使用:
(?s)
DOT_ALL 使 .
匹配任何字符,也 换行符。
.*?
最短匹配序列,不匹配"...]]>...]]>".
或者:在 DOM 树(保留 CDATA)中,您可以检索每个 XPath 的所有 CDATA 部分,并使用父元素删除空白兄弟。
在 Java 9 中,javax.xml.transform.Transformer
with OutputKeys.INDENT
处理 CDATA 标签的方式发生了变化。简而言之,在 Java 8 中,包含一些字符数据的名为 'test' 的标签将导致:
<test><![CDATA[data]]></test>
但是 Java 9 的结果相同
<test>
<![CDATA[data]]>
</test>
不一样XML。
我了解到(来自不再可用的来源)对于 Java 9 有一个使用 DocumentBuilderFactory
with setIgnoringElementContentWhitespace=true
的解决方法,但这不再适用于 Java 11.
有人知道在 Java 11 中处理这个问题的方法吗?我正在寻找一种方法来防止额外的换行符(但仍然能够格式化我的 XML),或者在解析 XML 时能够忽略它们(最好使用 SAX)。
遗憾的是,我不知道 CDATA 标记在我的应用程序中实际包含什么。它可能以白色 space 或换行符开头或结尾,因此我不能在读取 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
由于您的代码依赖于未指定的行为,额外的显式代码似乎更好:
您希望缩进为:
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*</", "></");
正则表达式使用:
(?s)
DOT_ALL 使.
匹配任何字符,也 换行符。.*?
最短匹配序列,不匹配"...]]>...]]>".
或者:在 DOM 树(保留 CDATA)中,您可以检索每个 XPath 的所有 CDATA 部分,并使用父元素删除空白兄弟。