Serialization and Deserialization using Jackson ObjectMapper

JSON (JavaScript Object Notation) is an open standard, language independent, data interchange file format which uses human readable text to store and transfer serializable data objects as key-value pairs and is commonly used as means of communication between the client and the server in a client-server architecture.

There are some open-source libraries written in Java which help to parse, create or work with JSON in some other ways. One of the most popular among them is “Jackson”. Jackson is a collection of Java data-processing tools and is mainly popular for its capability to parse and generate JSON and bind data (from POJO to JSON and vice versa). It also has the capability to parse some other data formats too like CSV, Protobuf, XML, YAML etc. but here our focus will be on JSON.

Currently, Jackson is having 3 core modules:
1) jackson-core (a streaming module which defines a streaming API and includes JSON-specific implementations)
2) jackson-annotations (contains Jackson annotations and depends on jackson-core)
3) jackson-databind (provides data objects serialization support and depends on jackson-annotations)

The JSON Parsers provided by Jackson are:
ObjectMapper” and “JsonParser

The JSON Generators provided by Jackson are:
ObjectMapper” and “JsonGenerator

To use Jackson, add the below dependencies in build.gradle (for Gradle) or pom.xml (for Maven) or simply add the three jar files in your project class path.

build.gradle

compile group: 'com.fasterxml.jackson.core', name: 'jackson-core', version: '2.12.1'
compile group: 'com.fasterxml.jackson.core', name: 'jackson-annotations', version: '2.12.1'
compile group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.12.1'

pom.xml

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-core</artifactId>
    <version>2.12.1</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-annotations</artifactId>
    <version>2.12.1</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.12.1</version>
</dependency>

ObjectMapper
The “ObjectMapper” class (present in jackson-databind) will help to parse/create JSON. JSON parsing can be done from a JSON string, stream, url or file and an equivalent Java object/object graph can be created from the parsed JSON (deserialization). JSON creation can also be done from Java object/object graph by the ObjectMapper class (serialization). It is referred to as “ObjectMapper” because it maps Java objects to JSON and vice versa.

Parse JSON

Let us create a simple POJO class (Person) having the private fields – “name” and “age”, their public getters and setters.

public class Person {

	private String name;
	private int age;

	public void setName(String name) {
		this.name = name;
	}

	public String getName() {
		return name;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public int getAge() {
		return age;
	}
}

To parse a given JSON (from a JSON source like string/stream/file/url) and map it to an object of the Person class, let’s write the code:

package com.jacksonFeatures.objectMapper;

import com.fasterxml.jackson.databind.ObjectMapper;

public class ParseJSON {
	public static void main(String[] args) {
		ParseJSON parseJSONObject = new ParseJSON();
		Person person = parseJSONObject. parseJSONFromJSONString();
		System.out.println("Name: " + person.getName());
		System.out.println("Age: " + person.getAge());
	}

	public Person parseJSONFromJSONString() {
		Person person = null;
		try {
			String jsonString = "{ \"name\" : \"Sam\", \"age\" : 45 }";
			ObjectMapper objectMapper = new ObjectMapper();
			person = objectMapper.readValue(jsonString, Person.class);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return person;
	}
}

Here, in the above code, ObjectMapper maps the JSON fields in the JSON string (named “jsonString”) to the public getter and setter methods of the Java object (named “person”) by removing their “get” and “set” method prefixes and converting the first letter to lowercase via the “readValue” method.

To parse from a JSON file, we can use FileReader (with BufferedReader(optional)) or only a File object. Let’s create a JSON file named “JSONFile1.json” (within the “files” folder) with the following content inside it:
{“name”: “Tom”, “age”: 61}

And write the Java code to parse the same and output to the system console.

public Person parseJSONFromJSONFile() {
		Person person = null;
		try {
			File file = new File("./files/JSONFile1.json");
			FileReader fileReader = new FileReader(file);
			BufferedReader bufferedReader = new BufferedReader(fileReader);
			ObjectMapper objectMapper = new ObjectMapper();
			person = objectMapper.readValue(bufferedReader, Person.class);
bufferedReader.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return person;
	}

A JSON ArrayString/url/inputStream/byteArray object can also be passed as the first parameter in the readValue() method. For example:

JSON Array String
String jsonArrayString = “[{\”name\”:\”Richard\”, \”age\” : 22 }, {\”name\”:\”Harry\”, \”age\” : 29 }]”;Person[] person = objectMapper.readValue(jsonStringArray, Person[].class);

URL
URL url = new URL(“https:// JSONFile1.json”);
person = objectMapper.readValue(url, Person.class);

InputStream
InputStream inputStream = new InputStream(“./files/JSONFile1.json”);
person = objectMapper.readValue(inputStream, Person.class);

ByteArray
byte[]  byteArray = person = jsonString.getBytes();
objectMapper.readValue(byteArray, Person.class);

Custom Deserialization
If we want to deserialize a JSON string into a Java object in a customized way (not defined by the ObjectMapper class), we can add a custom deserializer (e.g. “PersonDeserializer”) to the ObjectMapper class by extending “StdDeserializer” which is a base class for common Jackson deserializers.

package com.jacksonFeatures.objectMapper;

import java.io.IOException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;

public class PersonDeserializer extends StdDeserializer<Person> {
	public PersonDeserializer(Class<?> clazz) {
		super(clazz);
	}
	@Override
	public Person deserialize(JsonParser jsonParser, DeserializationContext deserializer) throws IOException {
		Person person = new Person();
		while (!jsonParser.isClosed()) {
			JsonToken jsonToken = jsonParser.nextToken();
			if (JsonToken.FIELD_NAME.equals(jsonToken)) {
				String fieldName = jsonParser.getCurrentName();
				jsonToken = jsonParser.nextToken();
				if ("name".equals(fieldName)) {
					person.setName(jsonParser.getValueAsString());
				} else if ("age".equals(fieldName)) {
					person.setAge(jsonParser.getValueAsInt());
				}
			}
		}
		return person;
	}
}

… and then perform the deserialization:

public Person parseJSONUsingCustomDeserialization() {
		Person person = null;
		try {
			String json = "{ \"name\" : \"Charlie\", \"age\" : 32 }";
			SimpleModule simpleModule = new SimpleModule("PersonDeserializer");
			simpleModule.addDeserializer(Person.class, new PersonDeserializer(Person.class));
			ObjectMapper objectMapper = new ObjectMapper();
			objectMapper.registerModule(simpleModule);
			person = objectMapper.readValue(json, Person.class);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return person;
	}

Generate JSON

Like parsing from JSON, we can also map Java objects to JSON (serialization) using the “ObjectMapper” class using the following methods:

writeValue()
writeValueAsString()
writeValueAsBytes()

Considering the Person class, let’s create an object and generate a JSON file (having the JSON content inside), JSON String and JSON byte array out of it.

public void generateJSONAsFileStringAndByteArray(File file) {
		try (FileOutputStream fileOutputStream = new FileOutputStream(file)) {
			Person person = new Person();
			person.setName("Raj");
			person.setAge(41);
			ObjectMapper objectMapper = new ObjectMapper();
			objectMapper.writeValue(fileOutputStream, person);
			String jsonString = objectMapper.writeValueAsString(person);
			System.out.println("JSON String: " + jsonString);
			byte[] jsonBytearray = objectMapper.writeValueAsBytes(person);
			System.out.println(Arrays.toString(jsonBytearray));
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

Like Custom Deserialization, we can also implement Custom Serialization using which we can serialize a Java Object to JSON differently to what Jackson supports by default. For that, Jackson helps us to set a custom serializer on the ObjectMapper class. This custom serializer is registered for a class, and will be called whenever the ObjectMapper will be asked to serialize a Person object.

package com.jacksonFeatures.objectMapper;

import java.io.IOException;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;

public class PersonSerializer extends StdSerializer<Person> {
	protected PersonSerializer(Class<Person> clazz) {
		super(clazz);
	}

	public void serialize(Person person, JsonGenerator jsonGenerator, SerializerProvider serializerProvider)
			throws IOException {
		jsonGenerator.writeStartObject();
		jsonGenerator.writeStringField("name", person.getName());
		jsonGenerator.writeNumberField("age", person.getAge());
		jsonGenerator.writeEndObject();
	}
}

… and then perform the serialization:

public void generateJSONUsingCustomSerialization() {
		try {
			PersonSerializer personSerializer = new PersonSerializer(Person.class);
			ObjectMapper objectMapper = new ObjectMapper();
			SimpleModule module = new SimpleModule("PersonSerializer");
			module.addSerializer(Person.class, personSerializer);
			objectMapper.registerModule(module);
			Person person = new Person();
			person.setName("Steve");
			person.setAge(55);
			String jsonString = objectMapper.writeValueAsString(person);
			System.out.println(jsonString);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}