Jackson is a powerful library which can automatically serialize to and from JSON to Java and I have made extensive use of it in my projects. To integration with jackson you annotate the fields within your POJOs with the @JsonProperty annotation. Then using a JSON Mapper you can convert the POJOs to JSON and JSON to POJOs. For more details see my earlier article (Polymorphic JSON Serialization using Jackson). However, sometimes the default behavior of the jackson mapper falls short. In one of my projects I needed to serialize a Java class to a specific integer field within that object. This is not possible using the default ObjectMapper or its serialization config. This is where custom serializes come in using Json Serializer comes to the rescue. This article describes the use of custom serializers and deseralizers with the Jackson library.

Getting started

As a starting point I will use the code in my earlier article Polymorphic JSON Serialization using Jackson which can be downloaded at gitbub.

In the source files you downloaded open Child1.java, look for the SerializeMe property. It is annotated with the JSON Property and will there for be serialized to an integer. What if we wanted to write the string representation of the integer rather than a number. i.e. If the field value was 3 we would write it as “three”. The first step is to annotate the property with the @JsonSerialize and @JsonDeserialize annotations and provide the custom classes we want to use for serialization and deserialization as shown below.

@JsonSerialize(using = CustomSerializer.class)
@JsonDeserialize(using = CustomDeSerializer.class)
@JsonProperty
public int SerializeMe;

 

Implementing the Custom Serializer

In order to implement a custom serializer we must extend the JsonSerializer class and define the template type to be the type for our annotated field. In our case the annotated field is on “int” type. Primitive types are automatically boxed into their respective object types, hence we will be using the Integer type. We override the serialize method which will receive the value of the field in the value parameter. Using the input value we can define what output we want to generate and then write it using the generator parameter. The generator class has many functions for writing the various Json types, we will be using the writeString method for our serializer however a complete list of methods is available here. I am writing a a small converter which generates the string value of input integers, see code below.

public class CustomSerializer extends JsonSerializer<Integer> {

	@Override
	public void serialize(Integer value, JsonGenerator generator, 
		SerializerProvider provider) throws IOException,
			JsonProcessingException {

		if (value == 1) {
			generator.writeString("one");
		} else if (value == 2) {
			generator.writeString("two");
		} else if (value == 3) {
			generator.writeString("three");
		} else {
			generator.writeString("A big number");
		}
	}
}

 

Implementing the Custom Deserializer

You can setup custom object deserialization in exactly the same fashion by extending the JsonDeserializer class and overriding the deserialize method. This method has a parser parameter which we can use to retrieve the Json data as shown below.

	@Override
	public Integer deserialize(JsonParser parser, DeserializationContext context) throws IOException,
			JsonProcessingException {

		String value = parser.getText();
		if (value.equals("one")) {
			return 1;
		} else if (value.equals("two")) {
			return 2;
		} else if (value.equals("three")) {
			return 3;
		} else {
			return 0;
		}
	}

 

Testing the code

Open up the Driver.java file from the downloaded sources and replace the main method with the code shown below. In this code we are creating and initializing an instance of the Child1 class and then writing it using a Jackson Object mapper. When you run this code you should see {"objectType":"Child1","SerializeMe":"three"}. If you delete the annotations from the Child one class and rerun the code you should see {"objectType":"Child1","SerializeMe":3}. When we deserialize we see that the value is again stored in the object as 3. This simple and admittedly contrived example shows the power of custom serializers. I have used these in several projects to format responses according to client requirements without impacting server Object designs.

public static void main(String[] args) {
	try {
		ObjectMapper oMapper = new ObjectMapper();

		Child1 child1 = new Child1();
		child1.SerializeMe = 3;
		child1.dontSerializeMe = 12;
		String outputChild1 = oMapper.writeValueAsString(child1);
		System.out.println(outputChild1);
		ParentClass inputChild1 = oMapper.readValue(outputChild1, ParentClass.class);
		System.out.println(((Child1) inputChild1).SerializeMe);
	} catch (Exception ex) {
		ex.printStackTrace();
	}
}

 

Source code

The source code for this project is released under the BSD License and can be downloaded at github.

External Links