Как преобразовать XML-документ с помощью CDATA с помощью JDOM2?

Исходный документ:

<content><![CDATA[>&< test]]></content>

Документ XSLT (cdata-transformation.xslt):

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.1" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" cdata-section-elements="transformed" />

  <xsl:template match="/content">
    <transformed>
      <xsl:value-of select="." />
    </transformed>
  </xsl:template>
</xsl:stylesheet>

Желаемый результат:

<?xml version="1.0" encoding="UTF-8"?>
<transformed><![CDATA[>&< test]]></transformed>

Фактический результат:

<?xml version="1.0" encoding="UTF-8"?>
<transformed>&gt;&amp;&lt; test</transformed>

Код, используемый для тестирования с использованием JDOM2:

import java.io.IOException;
import java.io.InputStream;

import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;

import org.jdom2.CDATA;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.input.SAXBuilder;
import org.jdom2.output.XMLOutputter;
import org.jdom2.transform.JDOMResult;
import org.jdom2.transform.JDOMSource;
import org.junit.Test;

public class CdataTransformationTest {

    @Test
    public void learning_cdataTransformationWithJdom() throws Exception {
        Document xslt = loadResource("xslt/cdata-transformation.xslt");
        Document source = new Document(new Element("content")
                .addContent(new CDATA(">&< test")));

        Document transformed = transform(source, xslt);

        XMLOutputter outputter = new XMLOutputter();
        System.out.println(outputter.outputString(transformed));
    }

    private static Document transform(Document sourceDoc, Document xsltDoc) throws TransformerException {
        JDOMSource source = new JDOMSource(sourceDoc);
        JDOMResult result = new JDOMResult();

        Transformer transformer = TransformerFactory.newInstance()
                .newTransformer(new JDOMSource(xsltDoc));

        transformer.transform(source, result);

        return result.getDocument();
    }

    private static Document loadResource(String resource) throws IOException, JDOMException {
        ClassLoader classloader = Thread.currentThread().getContextClassLoader();
        InputStream inputStream = classloader.getResourceAsStream(resource);
        if (inputStream != null) {
            try {
                SAXBuilder builder = new SAXBuilder();
                return builder.build(inputStream);
            } finally {
                inputStream.close();
            }
        } else {
            return null;
        }
    }
}

Используемая версия JDOM:

<dependency>
    <groupId>org.jdom</groupId>
    <artifactId>jdom2</artifactId>
    <version>2.0.6</version>
</dependency>

Используемый процессор XSLT:

<dependency>
    <groupId>xalan</groupId>
    <artifactId>xalan</artifactId>
    <version>2.7.1</version>
</dependency>

Я искал способы сделать это, и лучшие ответы говорят, что для переноса содержимого в CDATA необходимо добавить имя тега в атрибут cdata-section-elements. Я не могу заставить это работать с JDOM, а также при использовании бесплатного онлайн-преобразователя XSL. Я также пробовал использовать саксон вместо ксалана, но с тем же результатом.

Почему это не работает? Что я здесь упускаю/делаю неправильно? JDOM игнорирует атрибут cdata-section-elements?

Я также попытался обернуть содержимое следующим образом:

<xsl:text disable-output-escaping="yes">&lt;![CDATA[</xsl:text>
<xsl:value-of select="." />
<xsl:text disable-output-escaping="yes">]]&gt;</xsl:text>

Но это приводит к нежелательному результату в JDOM, что затрудняет работу. Видно, когда вы устанавливаете outputer.getFormat().setIgnoreTrAXEscapingPIs(true);, и это выглядит очень уродливо при использовании красивого формата.

<?xml version="1.0" encoding="UTF-8"?>
<transformed>
  <?javax.xml.transform.disable-output-escaping?>
  &lt;![CDATA[
  <?javax.xml.transform.enable-output-escaping?>
  &gt;&amp;&lt; test
  <?javax.xml.transform.disable-output-escaping?>
  ]]&gt;
  <?javax.xml.transform.enable-output-escaping?>
</transformed>

person Morten Nyhaug    schedule 15.01.2016    source источник
comment
FWIW, ваш метод работает как с Xalan, так и с Saxon (см.: xsltransform.net/94rmq6A), поэтому проблема дело не в коде, а в процессоре.   -  person michael.hor257k    schedule 15.01.2016


Ответы (1)


Вы преобразовываетесь в JDOMResult, то есть в древовидное представление, а не в поток или файл. Директивы вывода, такие как cdata-section-elements, используются только тогда, когда XSLT-процессор сериализует результат в поток или файл, но не при построении результирующего дерева в памяти. Поэтому я думаю, что если вы хотите создать разделы CDATA в результате XSLT с помощью cdata-section-elements, вам нужно убедиться, что вы пишете в файл или поток или, по крайней мере, в StringWriter, тогда вы можете загрузить результат JDOM из этого файла или потока, соответственно созданного Нить.

Переписать метод преобразования в:

private static Document transform(Document sourceDoc, Document xsltDoc) throws JDOMException, IOException, TransformerException {
    StringWriter writer = new StringWriter();
    JDOMSource source = new JDOMSource(sourceDoc);
    Result result = new StreamResult(writer);

    Transformer transformer = TransformerFactory.newInstance()
            .newTransformer(new JDOMSource(xsltDoc));

    transformer.transform(source, result);

    SAXBuilder builder = new SAXBuilder();
    return builder.build(new StringReader(writer.toString()));
}
person Martin Honnen    schedule 15.01.2016
comment
Спасибо Мартин, я не знал об этом. Добавлен модифицированный код, необходимый для его работы в вашем ответе. Это дает желаемый результат. - person Morten Nyhaug; 15.01.2016