Обработка изменений в новых строках путем преобразования XML для CDATA из Java 8 в Java 11 - PullRequest
10 голосов
/ 25 апреля 2019

В Java 9 произошли изменения в способе javax.xml.transform.Transformer с OutputKeys.INDENT обрабатывать теги CDATA. Короче говоря, в Java 8 тег с именем «test», содержащий некоторые символьные данные, приведет к:

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

Но с Java 9 те же самые результаты в

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

Который не тот же XML. См. http://java9.wtf/xml-transformer/ для получения дополнительной информации.

Я понял, что для Java 9 был обходной путь, использующий DocumentBuilderFactory с 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
}

РЕДАКТИРОВАТЬ: Для дальнейшего использования, я отправил отчет об ошибке в Oracle: https://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8223291

1 Ответ

2 голосов
/ 29 апреля 2019

Поскольку ваш код основан на неуказанном поведении, дополнительный явный код выглядит лучше:

  • Вы хотите использовать отступы как:

    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) вы можете извлечь все разделы CDATA для XPath и удалить дочерние элементы с пробелами, используя родительский элемент.

...