Entity view (Content)

Hacking JSON (de)serialization in Mule

By rcarter
Aug. 4, 2014

Mule provides an easy to use JSON module that has a bunch of functionality for transforming, filtering and working with JSON documents. Under the hood a lot of the functionality uses the excellent Jackson framework, but most of the Jackson specific config is all abstracted away so you can just get up and running quickly. It's also easy to write custom (de)serialization for your objects with simple Jackson annotations or even write mixins for objects that are out of your control.

I have seen quite a few questions come up in the Mule community around customizing the JSON transformers to handle special inputs and outputs such as empty collection handling and string/numeric handling. Jackson itself provides a set of general global (de)serialization configuration properties that cover most of these use-cases. However these values are defaulted under the hood by Mule and there comes a time where some of this abstracted config assumes certain settings you want to change. For example, Jackson provides a bunch of (de)serialization properties that instruct the JSON parser on how to handle certain objects or fields such as instructions to write all number fields as Strings or to disable writing empty collections and so on. These properties are usually set on the Jackson ObjectMapper either directly or by configuring the SerializationConfig and DeserializationConfig objects on the ObjectMapper class.

Mule provides a simple way to reference a custom ObjectMapper from the majority of it's JSON components.It also provides a global json:mapper element to configure mixins. However, this does not allow you to access all the settings Jackson has to offer. Luckily, Mule's Spring support allows us to construct objects ourselves and reference them from Mule components. This allows us to construct an ObjectMapper instance ourselves and use it lieu of the default. The following example shows how you can setup a custom ObjectMapper and override some of it's global settings.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<mule xmlns:core="http://www.mulesoft.org/schema/mule/core"
	xmlns:json="http://www.mulesoft.org/schema/mule/json" xmlns="http://www.mulesoft.org/schema/mule/core"
	xmlns:spring="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="
	http://www.mulesoft.org/schema/mule/core http://www.mulesoft.org/schema/mule/core/current/mule.xsd
	http://www.mulesoft.org/schema/mule/json http://www.mulesoft.org/schema/mule/json/current/mule-json.xsd
	http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-current.xsd">
 
	<spring:beans>
		<spring:bean id="jacksonObjectMapper" class="org.codehaus.jackson.map.ObjectMapper" />
		<spring:bean
			class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
			<spring:property name="targetObject" ref="jacksonObjectMapper" />
			<spring:property name="targetMethod" value="configure" />
			<spring:property name="arguments">
				<spring:list>
					<spring:value>WRITE_NUMBERS_AS_STRINGS</spring:value>
					<spring:value>true</spring:value>
				</spring:list>
			</spring:property>
		</spring:bean>
	</spring:beans>
 
	<flow name="json" >
		<poll frequency="10000">
			<set-payload value="#[ ['numericField' : 1234] ]" />
		</poll>
		
		<json:object-to-json-transformer mapper-ref="jacksonObjectMapper" />
		
		<logger level="INFO" message="#[payload]" />
	</flow>
</mule>

In this example we are simply polling every ten seconds and setting the payload to a single entry map where the entry's value is numeric. Using the default ObjectMapper, this will log the produced JSON as follows:

INFO[org.mule.api.processor.LoggerMessageProcessor]{"numericField":1234}

This output is correct. However, some random API we need to send this JSON to, expects everything as Strings and we don't want to change all out integers in our model to Strings. So instead construct the custom ObjectMapper ourselves using a simple Spring bean and then we use Spring's MethodInvokingFactoryBean to call the "configure" method instructing the ObjectMapper to write all numbers as Strings. Then we isntruct the json transformer to use our custom ObjectMapper via the "mapper-ref" attribute. Running this flow should output the following JSON string:

INFO[org.mule.api.processor.LoggerMessageProcessor]{"numericField":"1234"}

There's lots of other settings you can fiddle with. But beware that Mule uses the older org.codehaus Jackson rather than the newer com.fasterxml Jackson which has a lot more available configuration properties. Take a look at the JavaDoc to find out more.

Post Tags: