Friday, January 20, 2012

Spring JAXB with CDATA Elements


I wanted to configure the Jaxb2Marshaller to support CDATA elements. This example works with Spring 3.1 and Java 6, using the Java 6 restricted XML parser classes to configure the CDATA elements.



<bean id="marshaller" class="org.springbyexample.marshaller.CdataJaxb2Marshaller">
     <property name="cdataElements">
         <list>
             <value>^value</value>
         </list>
     </property>
...


import java.io.IOException;
import java.io.OutputStream;

import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.transform.Result;
import javax.xml.transform.stream.StreamResult;

import org.apache.commons.lang.ArrayUtils;
import org.springframework.oxm.MarshallingFailureException;
import org.springframework.oxm.XmlMappingException;
import org.springframework.oxm.jaxb.Jaxb2Marshaller;
import org.springframework.oxm.mime.MimeContainer;


public class CdataJaxb2Marshaller extends Jaxb2Marshaller {
    
    private String[] cdataElements;
    
    public String[] getCdataElements() {
        return cdataElements;
    }

    public void setCdataElements(String[] cdataElements) {
        this.cdataElements = cdataElements;
    }
    
    @Override
    public void marshal(Object graph, Result result, MimeContainer mimeContainer) throws XmlMappingException {
        if (ArrayUtils.isNotEmpty(cdataElements)) {
            try {
                Marshaller marshaller = createMarshaller();
    
                com.sun.org.apache.xml.internal.serialize.XMLSerializer serializer =
                        createXMLSerializer(cdataElements, ((StreamResult)result).getOutputStream());
                marshaller.marshal(graph, serializer.asContentHandler());
            } catch (IOException e) {
                throw new MarshallingFailureException(e.getMessage(), e);
            } catch (JAXBException ex) {
                throw convertJaxbException(ex);
            }
        } else {
            super.marshal(graph, result);
        }
    }

    @SuppressWarnings("restriction")
    private com.sun.org.apache.xml.internal.serialize.XMLSerializer createXMLSerializer(String[] cDataElements, OutputStream cOut) {
        // This code is from a sample online: http://jaxb.java.net/faq/JaxbCDATASample.java
        // configure an OutputFormat to handle CDATA
        com.sun.org.apache.xml.internal.serialize.OutputFormat of = new com.sun.org.apache.xml.internal.serialize.OutputFormat();

        // specify which of your elements you want to be handled as CDATA.
        // The use of the '^' between the namespaceURI and the localname
        // seems to be an implementation detail of the xerces code.
        // When processing xml that doesn't use namespaces, simply omit the
        // namespace prefix as shown in the third CDataElement below.
        of.setCDataElements(cDataElements); //

        // set any other options you'd like
        of.setPreserveSpace(true);
        of.setIndenting(true);
        of.setPreserveSpace(false);

        // create the serializer
        com.sun.org.apache.xml.internal.serialize.XMLSerializer serializer = new com.sun.org.apache.xml.internal.serialize.XMLSerializer(of);
        serializer.setOutputByteStream(cOut);

        return serializer;
    }

}

No comments: