Introduction

When marshalling Java objects to XML documents using JAXB, the default behavior for the Marshaller and Unmarshaller is to respectively escape and unescape the XML special characters. Sometimes, there is a specific need to have more control over this process to either change it or simply disable it. This post shows how to control this behavior.

Specifying a custom CharacterEscapeHandler in JAXB

Special character escaping inside the Marshaller is controlled using an implementation of a CharacterEscapeHandler. Changing the Marshaller escaping behavior is achieved by providing your own implementation as such:

Marshaller m = jcb.createMarshaller();
m.setProperty(
	"com.sun.xml.bind.marshaller.CharacterEscapeHandler",
	new CustomCharacterEscapeHandler());

where CustomCharacterEscapeHandler is a class whose signature is:

public class CustomCharacterEscapeHandler implements CharacterEscapeHandler

Disable character escaping in JAX-B

Providing an instance of the following NullCharacterEscapeHanlder will systematically disable character escaping :

public class NullCharacterEscapeHandler implements CharacterEscapeHandler {

	public NullCharacterEscapeHandler() {
		super();
	}

	/**
	 * @param ch The array of characters.
	 * @param start The starting position.
	 * @param length The number of characters to use.
	 * @param isAttVal true if this is an attribute value literal.
	 */
	public void escape(char[] ch, int start, int length, boolean isAttVal, Writer writer) throws IOException {
		writer.write( ch, start, length );
	}
}

Complete control over character escaping in JAXB

The following implementations shows how to escape all XML special characters. This implementation is very similar to the one propvided by the Sun Marshaller implementation except that it will also escape single quotes.

public class CompleteCharacterEscapeHandler implements CharacterEscapeHandler {

	public CompleteCharacterEscapeHandler() {
		super();
	}

	/**
	 * @param ch The array of characters.
	 * @param start The starting position.
	 * @param length The number of characters to use.
	 * @param isAttVal true if this is an attribute value literal.
	 */
	public void escape(char[] ch, int start, int length, boolean isAttVal, Writer out) throws IOException {
		int limit = start + length;
		for (int i = start; i < limit; i++) {
			char c = ch[i];
			if (c == '&' || c == '<' || c == '>' || (c == '\"' && isAttVal)
					|| (c == '\'' && isAttVal)) {
				if (i != start) {
					out.write(ch, start, i - start);
				}
				start = i + 1;
				switch (ch[i]) {
					case '&':
						out.write("&");
						break;

					case '<':
						out.write("<");
						break;

					case '>':
						out.write(">");
						break;

					case '\"':
						out.write(""");
						break;

					case '\'':
						out.write("'");
						break;
				}
			}
		}
		if (start != limit) {
			out.write(ch, start, limit - start);
		}
	}
}

Setting the CharacterEscapeHandler in Apache CXF

In a server side environment like those used for web services, you do not create the Marshaller yourself, it is usually provided by the server layer.
In that case, setting the CharacterEscapeHandler implementation is pretty much the same as above, you still have to set the appropriate property. The trick is to find how your server allows you to do so.

With Apache CXF for instance, this is done using the JAXB provider in the CXF configuration. In a Spring environment this gives:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:jaxrs="http://cxf.apache.org/jaxrs"
       xmlns:jaxws="http://cxf.apache.org/jaxws"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
       http://cxf.apache.org/jaxrs http://cxf.apache.org/schemas/jaxrs.xsd
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
       http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd">

    <import resource="classpath:META-INF/cxf/cxf.xml"/>
    <import resource="classpath:META-INF/cxf/cxf-extension-jaxrs-binding.xml"/>
    <import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" />
    <import resource="classpath:META-INF/cxf/cxf-servlet.xml"/>

    <jaxrs:server id="restContainer" address="/">

		<jaxrs:providers>
			<ref bean="jaxbProvider" />
		</jaxrs:providers>

    </jaxrs:server>

	<bean id="jaxbProvider" class="org.apache.cxf.jaxrs.provider.JAXBElementProvider">
		<property name="marshallerProperties">
			<map>
				<entry>
					<key>
						<value>com.sun.xml.bind.marshaller.CharacterEscapeHandler</value>
					</key>

					<ref bean="cdataCharacterEscapeHandler" />
				</entry>
			</map>
		</property>
	</bean>

	<bean id="cdataCharacterEscapeHandler" class="com.example.app.CustomCharacterEscapeHandler"/>

</beans>
Advertisements