1. Overview
This content is focused on understanding the Jackson JSON Parser ObjectMapper class – and how to serialize Java objects into JSON and deserialize JSON string into Java objects. To get a more vivid understanding about Jackson library in general, this Jackson Tutorial is a good place to start, let's dive in to the world of Object Parsing with Jackson.
This content is focused on understanding the Jackson JSON Parser ObjectMapper class – and how to serialize Java objects into JSON and deserialize JSON string into Java objects. To get a more vivid understanding about Jackson library in general, this Jackson Tutorial is a good place to start, let's dive in to the world of Object Parsing with Jackson.
2. Add Dependencies
There are two major ways amongst other to add dependencies in any java project, we will be sticking to gradle and maven.
Let’s first add the following dependencies to pom.xml:
Maven:
1
2
3
4
5
<
dependency
>
<
groupId
>com.fasterxml.jackson.core</
groupId
>
<
artifactId
>jackson-databind</
artifactId
>
<
version
>2.9.8</
version
>
</
dependency
>
Gradle:
1
compile group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.9.8'
This dependency will transitively add the following libraries to the classpath of your java project:
- jackson-annotations-2.9.8.jar
- jackson-core-2.9.8.jar
- jackson-databind-2.9.8.jar
Be certain to always use the latest versions as seen the mvnrepository for Jackson databind or any other dependency you may need
There are two major ways amongst other to add dependencies in any java project, we will be sticking to gradle and maven.
Let’s first add the following dependencies to pom.xml:
Maven:
Let’s first add the following dependencies to pom.xml:
Maven:
1
2
3
4
5
| < dependency > < groupId >com.fasterxml.jackson.core</ groupId > < artifactId >jackson-databind</ artifactId > < version >2.9.8</ version > </ dependency > |
Gradle:
1
| compile group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.9.8' |
This dependency will transitively add the following libraries to the classpath of your java project:
- jackson-annotations-2.9.8.jar
- jackson-core-2.9.8.jar
- jackson-databind-2.9.8.jar
Be certain to always use the latest versions as seen the mvnrepository for Jackson databind or any other dependency you may need
3. Reading and Writing Using ObjectMapper
Codes don't lie let’s start with the basic read and write operations.
The simple readValue API of the ObjectMapper is a good entry-point; this can be used to parse or deserialize JSON content into a Java object.
On the write side of things – the writeValue API can be used to serialize any Java object to a JSON output.
We’ll use the following Car class with two fields as the object to serialize or deserialize throughout this article:
1
2
3
4
5
public
class
Car {
private
String color;
private
String type;
// standard getters setters
}
Codes don't lie let’s start with the basic read and write operations.
The simple readValue API of the ObjectMapper is a good entry-point; this can be used to parse or deserialize JSON content into a Java object.
On the write side of things – the writeValue API can be used to serialize any Java object to a JSON output.
We’ll use the following Car class with two fields as the object to serialize or deserialize throughout this article:
1
2
3
4
5
| public class Car { private String color; private String type; // standard getters setters } |
3.1. Java Object to JSON
A simple but would suffice example of serializing Java Object into a JSON object using the writeValue method of ObjectMapper class:
1
2
3
ObjectMapper objectMapper =
new
ObjectMapper();
Car car =
new
Car(
"yellow"
,
"renault"
);
objectMapper.writeValue(
new
File(
"path/to/car.json"
), car);
The output of the above can be seen in the file as follows:
1
{
"color"
:
"red"
,
"type"
:
"Toyota"
}
The methods writeValueAsString and writeValueAsBytes of ObjectMapper class generates a JSON from a Java object and returns the generated JSON object as a string or as a byte array:
1
String carAsString = objectMapper.writeValueAsString(car);
A simple but would suffice example of serializing Java Object into a JSON object using the writeValue method of ObjectMapper class:
1
2
3
| ObjectMapper objectMapper = new ObjectMapper(); Car car = new Car( "yellow" , "renault" ); objectMapper.writeValue( new File( "path/to/car.json" ), car); |
The output of the above can be seen in the file as follows:
1
| { "color" : "red" , "type" : "Toyota" } |
The methods writeValueAsString and writeValueAsBytes of ObjectMapper class generates a JSON from a Java object and returns the generated JSON object as a string or as a byte array:
1
| String carAsString = objectMapper.writeValueAsString(car); |
3.2. JSON to Java Object
Below is a simple example of JSON string converted to Java object using the ObjectMapper class:
1
2
String carJson =
"{ \"color\" : \"gray\", \"type\" : \"Audi\" }"
;
Car car = objectMapper.readValue(carJson, Car.
class
);
The readValue method/function accepts the JSON string and the class to deserialize to, but also accepts other forms of arguments like a file containing JSON string:
1
Car car = objectMapper.readValue(
new
File(
"path/to/json_car.json"
), Car.
class
);
or a URL:
1
Car car = objectMapper.readValue(
new
URL(
"url/to/json_car.json"
), Car.
class
);
Below is a simple example of JSON string converted to Java object using the ObjectMapper class:
1
2
| String carJson = "{ \"color\" : \"gray\", \"type\" : \"Audi\" }" ; Car car = objectMapper.readValue(carJson, Car. class ); |
The readValue method/function accepts the JSON string and the class to deserialize to, but also accepts other forms of arguments like a file containing JSON string:
1
| Car car = objectMapper.readValue( new File( "path/to/json_car.json" ), Car. class ); |
or a URL:
1
| Car car = objectMapper.readValue( new URL( "url/to/json_car.json" ), Car. class ); |
3.3. JSON to Jackson JsonNode
Alternatively, a JSON Object or string can be parsed into a JsonNode object which is a class from and used to retrieve data from a given specified node:
1
2
3
4
String carJson =
"{ \"color\" : \"Blue\", \"type\" : \"Bentley\" }"
;
JsonNode jsonNode = objectMapper.readTree(carJson);
String color = jsonNode.get(
"color"
).asText();
// Output: color -> Blue
Alternatively, a JSON Object or string can be parsed into a JsonNode object which is a class from and used to retrieve data from a given specified node:
1
2
3
4
| String carJson = "{ \"color\" : \"Blue\", \"type\" : \"Bentley\" }" ; JsonNode jsonNode = objectMapper.readTree(carJson); String color = jsonNode.get( "color" ).asText(); // Output: color -> Blue |
3.4. Creating Java List of Objects from JSON Array String
A JSON in the form of an array can be parsed into a Java object list by parsing in the following way:
1
2
3
String jsonCarArray =
"[{ \"color\" : \"Black\", \"type\" : \"BMW\" }, { \"color\" : \"Red\", \"type\" : \"FIAT\" }]"
;
List<Car> listCar = objectMapper.readValue(jsonCarArray,
new
TypeReference<List<Car>>(){});
A JSON in the form of an array can be parsed into a Java object list by parsing in the following way:
1
2
3
| String jsonCarArray = "[{ \"color\" : \"Black\", \"type\" : \"BMW\" }, { \"color\" : \"Red\", \"type\" : \"FIAT\" }]" ; List<Car> listCar = objectMapper.readValue(jsonCarArray, new TypeReference<List<Car>>(){}); |
3.5. Creating Java Map from JSON String
A JSON in the form of string can be parsed into a Java Map object by parsing in the following way:
1
2
3
String json =
"{ \"color\" : \"Black\", \"type\" : \"BMW\" }"
;
Map<String, Object> map
= objectMapper.readValue(json,
new
TypeReference<Map<String,Object>>(){});
A JSON in the form of string can be parsed into a Java Map object by parsing in the following way:
1
2
3
| String json = "{ \"color\" : \"Black\", \"type\" : \"BMW\" }" ; Map<String, Object> map = objectMapper.readValue(json, new TypeReference<Map<String,Object>>(){}); |
4. Advanced Features
Jackson has some great strength and features, some of which is the highly customizable serialization and deserialization process.
In this section, we will go through some advanced features where the input or the output JSON response can be different from the object which generates or consumes the response.
Jackson has some great strength and features, some of which is the highly customizable serialization and deserialization process.
In this section, we will go through some advanced features where the input or the output JSON response can be different from the object which generates or consumes the response.
4.1. Configuring Serialization or Deserialization Feature
While converting JSON objects to Java classes, in case the JSON string has some new fields, then the default process will run in an exception:
1
2
String jsonString
=
"{ \"color\" : \"Black\", \"type\" : \"Fiat\", \"year\" : \"1970\" }"
;
The JSON string in the above example in the default parsing process to the Java object for the Class Carwill result in the UnrecognizedPropertyException exception.
Through the configure method we can extend the default process to ignore the new fields:
1
2
3
4
5
6
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,
false
);
Car car = objectMapper.readValue(jsonString, Car.
class
);
JsonNode jsonNodeRoot = objectMapper.readTree(jsonString);
JsonNode jsonNodeYear = jsonNodeRoot.get(
"year"
);
String year = jsonNodeYear.asText();
Yet another option is based on the FAIL_ON_NULL_FOR_PRIMITIVES which defines if the null values for primitive values are allowed:
1
objectMapper.configure(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES,
false
);
Similarly, FAIL_ON_NUMBERS_FOR_ENUM controls if enum values are allowed to be serialized/deserialized as numbers
1
objectMapper.configure(DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS,
false
);
You can find the comprehensive list of serialization and deserialization features on the official site.
While converting JSON objects to Java classes, in case the JSON string has some new fields, then the default process will run in an exception:
1
2
| String jsonString = "{ \"color\" : \"Black\", \"type\" : \"Fiat\", \"year\" : \"1970\" }" ; |
The JSON string in the above example in the default parsing process to the Java object for the Class Carwill result in the UnrecognizedPropertyException exception.
Through the configure method we can extend the default process to ignore the new fields:
1
2
3
4
5
6
| objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false ); Car car = objectMapper.readValue(jsonString, Car. class ); JsonNode jsonNodeRoot = objectMapper.readTree(jsonString); JsonNode jsonNodeYear = jsonNodeRoot.get( "year" ); String year = jsonNodeYear.asText(); |
Yet another option is based on the FAIL_ON_NULL_FOR_PRIMITIVES which defines if the null values for primitive values are allowed:
1
| objectMapper.configure(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES, false ); |
Similarly, FAIL_ON_NUMBERS_FOR_ENUM controls if enum values are allowed to be serialized/deserialized as numbers
1
| objectMapper.configure(DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS, false ); |
You can find the comprehensive list of serialization and deserialization features on the official site.
4.2. Creating Custom Serializer or Deserializer
Another essential feature of the ObjectMapper class is the ability to register custom serializer and deserializer. Custom serializer and deserializer are very useful in situations where the input or the output JSON response is different in structure than the Java class into which it must be serialized or deserialized.
Below is an example of custom JSON serializer:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public
class
CustomCarSerializer
extends
StdSerializer<Car> {
public
CustomCarSerializer() {
this
(
null
);
}
public
CustomCarSerializer(Class<Car> t) {
super
(t);
}
@Override
public
void
serialize(
Car car, JsonGenerator jsonGenerator, SerializerProvider serializer) {
jsonGenerator.writeStartObject();
jsonGenerator.writeStringField(
"car_brand"
, car.getType());
jsonGenerator.writeEndObject();
}
}
This custom serializer can be invoked like this:
1
2
3
4
5
6
7
ObjectMapper mapper =
new
ObjectMapper();
SimpleModule module =
new
SimpleModule(
"CustomCarSerializer"
,
new
Version(
1
,
0
,
0
,
null
,
null
,
null
));
module.addSerializer(Car.
class
,
new
CustomCarSerializer());
mapper.registerModule(module);
Car car =
new
Car(
"yellow"
,
"renault"
);
String carJson = mapper.writeValueAsString(car);
Here’s what the Car looks like (as JSON output) on the client side:
1
var
carJson = {
"car_brand"
:
"renault"
}
And here’s an example of a custom JSON deserializer:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public
class
CustomCarDeserializer
extends
StdDeserializer<Car> {
public
CustomCarDeserializer() {
this
(
null
);
}
public
CustomCarDeserializer(Class<?> vc) {
super
(vc);
}
@Override
public
Car deserialize(JsonParser parser, DeserializationContext deserializer) {
Car car =
new
Car();
ObjectCodec codec = parser.getCodec();
JsonNode node = codec.readTree(parser);
// try catch block
JsonNode colorNode = node.get(
"color"
);
String color = colorNode.asText();
car.setColor(color);
return
car;
}
}
This custom deserializer can be invoked in the following way:
1
2
3
4
5
6
7
String json =
"{ \"color\" : \"Black\", \"type\" : \"BMW\" }"
;
ObjectMapper mapper =
new
ObjectMapper();
SimpleModule module =
new
SimpleModule(
"CustomCarDeserializer"
,
new
Version(
1
,
0
,
0
,
null
,
null
,
null
));
module.addDeserializer(Car.
class
,
new
CustomCarDeserializer());
mapper.registerModule(module);
Car car = mapper.readValue(json, Car.
class
);
Another essential feature of the ObjectMapper class is the ability to register custom serializer and deserializer. Custom serializer and deserializer are very useful in situations where the input or the output JSON response is different in structure than the Java class into which it must be serialized or deserialized.
Below is an example of custom JSON serializer:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| public class CustomCarSerializer extends StdSerializer<Car> { public CustomCarSerializer() { this ( null ); } public CustomCarSerializer(Class<Car> t) { super (t); } @Override public void serialize( Car car, JsonGenerator jsonGenerator, SerializerProvider serializer) { jsonGenerator.writeStartObject(); jsonGenerator.writeStringField( "car_brand" , car.getType()); jsonGenerator.writeEndObject(); } } |
This custom serializer can be invoked like this:
1
2
3
4
5
6
7
| ObjectMapper mapper = new ObjectMapper(); SimpleModule module = new SimpleModule( "CustomCarSerializer" , new Version( 1 , 0 , 0 , null , null , null )); module.addSerializer(Car. class , new CustomCarSerializer()); mapper.registerModule(module); Car car = new Car( "yellow" , "renault" ); String carJson = mapper.writeValueAsString(car); |
Here’s what the Car looks like (as JSON output) on the client side:
1
| var carJson = { "car_brand" : "renault" } |
And here’s an example of a custom JSON deserializer:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| public class CustomCarDeserializer extends StdDeserializer<Car> { public CustomCarDeserializer() { this ( null ); } public CustomCarDeserializer(Class<?> vc) { super (vc); } @Override public Car deserialize(JsonParser parser, DeserializationContext deserializer) { Car car = new Car(); ObjectCodec codec = parser.getCodec(); JsonNode node = codec.readTree(parser); // try catch block JsonNode colorNode = node.get( "color" ); String color = colorNode.asText(); car.setColor(color); return car; } } |
This custom deserializer can be invoked in the following way:
1
2
3
4
5
6
7
| String json = "{ \"color\" : \"Black\", \"type\" : \"BMW\" }" ; ObjectMapper mapper = new ObjectMapper(); SimpleModule module = new SimpleModule( "CustomCarDeserializer" , new Version( 1 , 0 , 0 , null , null , null )); module.addDeserializer(Car. class , new CustomCarDeserializer()); mapper.registerModule(module); Car car = mapper.readValue(json, Car. class ); |
4.3. Handling Date Formats
The default serialization of java.util.Date produces a number i.e. epoch timestamp (number of milliseconds since January 1st, 1970, UTC). But this is not very human readable, and further conversion is needed to display human readable format.
Let us wrap the Car instance we used so far inside the Request class with the datePurchased property:
1
2
3
4
5
6
7
public
class
Request
{
private
Car car;
private
Date datePurchased;
// standard getters setters
}
To control the String format of a date, and set it to e.g. yyyy-MM-dd HH:mm a z, consider the following snippet:
1
2
3
4
5
ObjectMapper objectMapper =
new
ObjectMapper();
DateFormat df =
new
SimpleDateFormat(
"yyyy-MM-dd HH:mm a z"
);
objectMapper.setDateFormat(df);
String carAsString = objectMapper.writeValueAsString(request);
// output: {"car":{"color":"yellow","type":"renault"},"datePurchased":"2016-07-03 11:43 AM CEST"}
To learn more about serializing dates with Jackson, read our more in-depth write-up.
The default serialization of java.util.Date produces a number i.e. epoch timestamp (number of milliseconds since January 1st, 1970, UTC). But this is not very human readable, and further conversion is needed to display human readable format.
Let us wrap the Car instance we used so far inside the Request class with the datePurchased property:
1
2
3
4
5
6
7
| public class Request { private Car car; private Date datePurchased; // standard getters setters } |
To control the String format of a date, and set it to e.g. yyyy-MM-dd HH:mm a z, consider the following snippet:
1
2
3
4
5
| ObjectMapper objectMapper = new ObjectMapper(); DateFormat df = new SimpleDateFormat( "yyyy-MM-dd HH:mm a z" ); objectMapper.setDateFormat(df); String carAsString = objectMapper.writeValueAsString(request); // output: {"car":{"color":"yellow","type":"renault"},"datePurchased":"2016-07-03 11:43 AM CEST"} |
To learn more about serializing dates with Jackson, read our more in-depth write-up.
4.4. Handling Collections
Another small but useful feature available through DeserializationFeature is the ability to generate the type of collection desired from a JSON Array response, e.g. as an array:
1
2
3
4
5
6
String jsonCarArray =
"[{ \"color\" : \"Black\", \"type\" : \"BMW\" }, { \"color\" : \"Red\", \"type\" : \"FIAT\" }]"
;
ObjectMapper objectMapper =
new
ObjectMapper();
objectMapper.configure(DeserializationFeature.USE_JAVA_ARRAY_FOR_JSON_ARRAY,
true
);
Car[] cars = objectMapper.readValue(jsonCarArray, Car[].
class
);
// print cars
Or as a List:
1
2
3
4
5
String jsonCarArray =
"[{ \"color\" : \"Black\", \"type\" : \"BMW\" }, { \"color\" : \"Red\", \"type\" : \"FIAT\" }]"
;
ObjectMapper objectMapper =
new
ObjectMapper();
List<Car> listCar = objectMapper.readValue(jsonCarArray,
new
TypeReference<List<Car>>(){});
// print cars
More about handling collections with Jackson is available here.
Another small but useful feature available through DeserializationFeature is the ability to generate the type of collection desired from a JSON Array response, e.g. as an array:
1
2
3
4
5
6
| String jsonCarArray = "[{ \"color\" : \"Black\", \"type\" : \"BMW\" }, { \"color\" : \"Red\", \"type\" : \"FIAT\" }]" ; ObjectMapper objectMapper = new ObjectMapper(); objectMapper.configure(DeserializationFeature.USE_JAVA_ARRAY_FOR_JSON_ARRAY, true ); Car[] cars = objectMapper.readValue(jsonCarArray, Car[]. class ); // print cars |
Or as a List:
1
2
3
4
5
| String jsonCarArray = "[{ \"color\" : \"Black\", \"type\" : \"BMW\" }, { \"color\" : \"Red\", \"type\" : \"FIAT\" }]" ; ObjectMapper objectMapper = new ObjectMapper(); List<Car> listCar = objectMapper.readValue(jsonCarArray, new TypeReference<List<Car>>(){}); // print cars |
More about handling collections with Jackson is available here.
5. Conclusion
Jackson ObjectMapper is a solid and mature JSON serialization/deserialization library for Java. And the ObjectMapper API provides a straightforward way to parse and generate JSON response objects with a lot of flexibility and dynamic features.
The article discusses the main features which make the library so popular. The source code that accompanies the article can be found over on GitHub feel free to clone and edit as your needs require.
Jackson ObjectMapper is a solid and mature JSON serialization/deserialization library for Java. And the ObjectMapper API provides a straightforward way to parse and generate JSON response objects with a lot of flexibility and dynamic features.
The article discusses the main features which make the library so popular. The source code that accompanies the article can be found over on GitHub feel free to clone and edit as your needs require.
No comments:
Post a Comment