Morten Nyhaug
Morten Nyhaug

Reputation: 283

How to transform XML document with CDATA using JDOM2?

Source document:

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

XSLT document (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>

Wanted result:

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

Actual result:

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

Code used to test using 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 version used:

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

XSLT processor used:

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

I have searched around for ways to do this, and the best answers say that what is needed to wrap content in CDATA is to add the tag name in the cdata-section-elements attribute. I cannot get this to work with JDOM and not when using the Free online XSL Transformer either. I have also tried using saxon instead of xalan, but with the same result.

Why doesn't this work? What am I missing/doing wrong here? Is JDOM ignoring the cdata-section-elements attribute?

I have also tried wrapping the content like this:

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

But this produces an unwanted result in JDOM which makes it difficult to work with. Visible when you set the outputer.getFormat().setIgnoreTrAXEscapingPIs(true); and it looks really ugly when using pretty format.

<?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>

Upvotes: 2

Views: 1207

Answers (1)

Martin Honnen
Martin Honnen

Reputation: 167461

You are transforming to a JDOMResult, that is, a tree representation, and not to a stream or file. Output directives like cdata-section-elements are only used when the XSLT processor serializes the result to a stream or file, but not when building a result tree in memory. So I think if you want to construct CDATA sections as the result of XSLT with cdata-section-elements, you need to make sure you write to a file or stream or at least a StringWriter, then you could load the JDOM result from that file or stream respectively created String.

Rewriting the transform method to:

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()));
}

Upvotes: 3

Related Questions