CXF, JAXRS, XML and CDATA

I was in a need to export some XML tags as CDATA in my CXF XML @WebService that generates some XML for our partner.

We had a field that contained an URL and in that we have ampersand characters. By default these get turned into “&” but our specific XML partner was not OK with that so we had to adjust to CDATA. That turned out to be rather complex as I was hoping for something simple like @CDATA annotation – but as always, life is full of suprises.

Anyway, the trick is rather simple to make it work – We need to define our own XML Stream Writer and overwrite the default write method from “writeCharacters” to “writeCData”.

We had a very specific tags in mind only so we specified those as static string array.

You can do this like this:

import java.util.Arrays;

import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;

import org.apache.cxf.staxutils.DelegatingXMLStreamWriter;

/**
 * Simple CDATA XML Stream Writer that exports some items as CDATA
 */
public class CDataXMLStreamWriter extends DelegatingXMLStreamWriter {

	// All elements with these names will be turned into CDATA
	private static String[] CDATA_ELEMENTS = { "infoUrl", "description" };

	private String currentElementName;

	public CDataXMLStreamWriter(XMLStreamWriter del) {
		super(del);
	}

	@Override
	public void writeCharacters(String text) throws XMLStreamException {
		if (Arrays.asList(CDATA_ELEMENTS).contains(currentElementName)) {
			super.writeCData(text);
		} else {
			super.writeCharacters(text);
		}
	}

	public void writeStartElement(String prefix, String local, String uri) throws XMLStreamException {
		currentElementName = local;
		super.writeStartElement(prefix, local, uri);
	}
}

OK; So now we have this nice writer, but how do you use it? Well, we are running it all inside a CXF that’s running with Spring container so as it turns out, we need an “Interceptor” that would help us write stuff. You can make one easily like this.


import java.io.OutputStream;

import javax.xml.stream.XMLStreamWriter;

import org.apache.cxf.interceptor.AttachmentOutInterceptor;
import org.apache.cxf.message.Message;
import org.apache.cxf.phase.AbstractPhaseInterceptor;
import org.apache.cxf.phase.Phase;
import org.apache.cxf.staxutils.StaxUtils;

public class CDataWriterInterceptor extends AbstractPhaseInterceptor {

	public CDataWriterInterceptor() {
		super(Phase.PRE_STREAM);
		addAfter(AttachmentOutInterceptor.class.getName());
	}

	@Override
	public void handleMessage(Message message) {
		// Required for CDATA to working
		message.put("disable.outputstream.optimization", Boolean.TRUE);
		XMLStreamWriter writer = StaxUtils.createXMLStreamWriter(message.getContent(OutputStream.class));
		message.setContent(XMLStreamWriter.class, new CDataXMLStreamWriter(writer));
	}
}

And now, how do you use this interceptor? In your applicationContext.xml or similar Spring context file, define the interceptor and use it!
Note that the LoggingFeature is not required but I’ve left it in here so that you could easily enable logging to see the incoming and outgoing messages better.


	<bean id="CDataWriterInterceptor" class="com.amivarius.cxf.util.CDataWriterInterceptor" />

	<jaxrs:server .... >

		<jaxrs:features>
			<bean class="org.apache.cxf.feature.LoggingFeature" />
		</jaxrs:features>

		<jaxrs:outInterceptors>
			<ref bean="CDataWriterInterceptor" />
		</jaxrs:outInterceptors>

	</jaxrs:server>

And that’s it folks!

The output will be <![CDATA[]]> nicely 🙂

About Reigo Reinmets

Enterprise Software consultant with 9 years of experience in enterprise software world who’s working on various projects that are mostly based on Liferay. Most blog posts here are real-life findings and issues we’ve encountered while working on projects and Java tutorials on Liferay.

3 thoughts on “CXF, JAXRS, XML and CDATA

  1. HI,
    I’m following this code as guide to send an XML inside the CDATA, but the XML gets splitted in multiple CDATA parts, something like:

    Do you know about any issue with this. I think it can be due to /n characters, because when I remove them all the xml is in only one CDATA block. The issue with this is that my XML is signed and I can’t modify it because the signature breaks, and I don’t have access to modify it before it’s signed.

    Excellent post by the way!
    Thanks

Leave a Reply

Your email address will not be published. Required fields are marked *