How to generate CDATA block using JAXB? How to generate CDATA block using JAXB? xml xml

How to generate CDATA block using JAXB?


Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.

If you are using MOXy as your JAXB provider then you can leverage the @XmlCDATA extension:

package blog.cdata;import javax.xml.bind.annotation.XmlRootElement;import org.eclipse.persistence.oxm.annotations.XmlCDATA;@XmlRootElement(name="c")public class Customer {   private String bio;   @XmlCDATA   public void setBio(String bio) {      this.bio = bio;   }   public String getBio() {      return bio;   }}

For More Information


Use JAXB's Marshaller#marshal(ContentHandler) to marshal into a ContentHandler object. Simply override the characters method on the ContentHandler implementation you are using (e.g. JDOM's SAXHandler, Apache's XMLSerializer, etc):

public class CDataContentHandler extends (SAXHandler|XMLSerializer|Other...) {    // see http://www.w3.org/TR/xml/#syntax    private static final Pattern XML_CHARS = Pattern.compile("[<>&]");    public void characters(char[] ch, int start, int length) throws SAXException {        boolean useCData = XML_CHARS.matcher(new String(ch,start,length)).find();        if (useCData) super.startCDATA();        super.characters(ch, start, length);        if (useCData) super.endCDATA();    }}

This is much better than using the XMLSerializer.setCDataElements(...) method because you don't have to hardcode any list of elements. It automatically outputs CDATA blocks only when one is required.


Solution Review:

  • The answer of fred is just a workaround which will fail while validating the content when the Marshaller is linked to a Schema because you modify only the string literal and do not create CDATA sections. So if you only rewrite the String from foo to <![CDATA[foo]]> the length of the string is recognized by Xerces with 15 instead of 3.
  • The MOXy solution is implementation specific and does not work only with the classes of the JDK.
  • The solution with the getSerializer references to the deprecated XMLSerializer class.
  • The solution LSSerializer is just a pain.

I modified the solution of a2ndrade by using a XMLStreamWriter implementation. This solution works very well.

XMLOutputFactory xof = XMLOutputFactory.newInstance();XMLStreamWriter streamWriter = xof.createXMLStreamWriter( System.out );CDataXMLStreamWriter cdataStreamWriter = new CDataXMLStreamWriter( streamWriter );marshaller.marshal( jaxbElement, cdataStreamWriter );cdataStreamWriter.flush();cdataStreamWriter.close();

Thats the CDataXMLStreamWriter implementation. The delegate class simply delegates all method calls to the given XMLStreamWriter implementation.

import java.util.regex.Pattern;import javax.xml.stream.XMLStreamException;import javax.xml.stream.XMLStreamWriter;/** * Implementation which is able to decide to use a CDATA section for a string. */public class CDataXMLStreamWriter extends DelegatingXMLStreamWriter{   private static final Pattern XML_CHARS = Pattern.compile( "[&<>]" );   public CDataXMLStreamWriter( XMLStreamWriter del )   {      super( del );   }   @Override   public void writeCharacters( String text ) throws XMLStreamException   {      boolean useCData = XML_CHARS.matcher( text ).find();      if( useCData )      {         super.writeCData( text );      }      else      {         super.writeCharacters( text );      }   }}