1. Overview
In this article, we'll look at serialization and deserialization of Java maps using Jackson.
We'll illustrate how to serialize and deserialize Map<String, String>, Map<Object, String>, and Map<Object, Object> _to and from JSON-formatted _Strings.
2. Maven Configuration
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.8.7</version>
</dependency>
You can get the latest version of Jackson here.
3. Serialization
Serialization converts a Java object into a stream of bytes, which can be persisted or shared as needed. Java Maps are collections which map a key Object to a value Object and are often the least intuitive objects to serialize.
3.1. Map<String, String> Serialization
For the simple case, let's create a Map<String, String> and serialize it to JSON:
Map<String, String> map = new HashMap<>();
map.put("key", "value");
ObjectMapper mapper = new ObjectMapper();
String jsonResult = mapper.writerWithDefaultPrettyPrinter()
.writeValueAsString(map);
ObjectMapper _is Jackson's serialization mapper, which allows us to serialize our _map and write it out as a pretty-printed JSON String, using the toString() method in String:
{
"key" : "value"
}
3.2. Map<Object, String> Serialization
You can serialize a map containing a custom Java class with a few extra steps. Let's create a MyPair class to represent a pair of related String objects.
Note: the getters/setters should be public, and we annotate toString() _with @JsonValue to ensure Jackson uses this custom _toString() when serializing:
public class MyPair {
private String first;
private String second;
@Override
@JsonValue
public String toString() {
return first + " and " + second;
}
// standard getter, setters, equals, hashCode, constructors
}
Now let's tell Jackson how to serialize MyPair by extending Jackson's JsonSerializer:
public class MyPairSerializer extends JsonSerializer<MyPair> {
private ObjectMapper mapper = new ObjectMapper();
@Override
public void serialize(MyPair value,
JsonGenerator gen,
SerializerProvider serializers)
throws IOException, JsonProcessingException {
StringWriter writer = new StringWriter();
mapper.writeValue(writer, value);
gen.writeFieldName(writer.toString());
}
}
JsonSerializer, as the name suggests, serializes MyPair to JSON using MyPair's toString() method. Jackson provides many Serializer classes to fit your serialization requirements.
We apply MyPairSerializer to our Map<MyPair, String> with the @JsonSerialize_annotation. Note that we've only told Jackson how to serialize _MyPair _because it already knows how to serialize _String:
@JsonSerialize(keyUsing = MyPairSerializer.class)
Map<MyPair, String> map;
Let's test our map serialization:
map = new HashMap<>();
MyPair key = new MyPair("Abbott", "Costello");
map.put(key, "Comedy");
String jsonResult = mapper.writerWithDefaultPrettyPrinter()
.writeValueAsString(map);
The serialized JSON output is:
{
"Abbott and Costello" : "Comedy"
}
3.3. Map<Object, Object> Serialization
The most complex case is serializing a Map<Object, Object>, but most of the work is already done. Let's use Jackson's _MapSerializer _for our map, and _MyPairSerializer_from the previous section for the map's key and value types:
@JsonSerialize(keyUsing = MapSerializer.class)
Map<MyPair, MyPair> map;
@JsonSerialize(keyUsing = MyPairSerializer.class)
MyPair mapKey;
@JsonSerialize(keyUsing = MyPairSerializer.class)
MyPair mapValue;
Let's test out serializing our Map<MyPair, MyPair>:
mapKey = new MyPair("Abbott", "Costello");
mapValue = new MyPair("Comedy", "1940s");
map.put(mapKey, mapValue);
String jsonResult = mapper.writerWithDefaultPrettyPrinter()
.writeValueAsString(map);
The serialized JSON output, using MyPair's toString() method, is:
{
"Abbott and Costello" : "Comedy and 1940s"
}
4. Deserialization
Deserialization converts a stream of bytes into a Java object that we can use in code. In this section, we'll deserialize JSON input into _Maps _of different signatures.
4.1. Map<String, String> Deserialization
For the simple case, let's take a JSON-formatted input string and convert it to a Map<String, String> Java collection:
String jsonInput = "{\"key\": \"value\"}";
TypeReference<HashMap<String, String>> typeRef
= new TypeReference<HashMap<String, String>>() {};
Map<String, String> map = mapper.readValue(jsonInput, typeRef);
We use Jackson's ObjectMapper as we did for serialization, using readValue() _to process the input. Also, note our use of Jackson's _TypeReference, which we'll use in all of our deserialization examples, to describe the type of our destination Map. Here is the _toString() _representation of our map:
{key=value}
4.2. Map<Object, String> Deserialization
Now, let's change our input JSON and the TypeReference _of our destination to_Map<MyPair, String>:
String jsonInput = "{\"Abbott and Costello\" : \"Comedy\"}";
TypeReference<HashMap<MyPair, String>> typeRef
= new TypeReference<HashMap<MyPair, String>>() {};
Map<MyPair,String> map = mapper.readValue(jsonInput, typeRef);
We need to create a constructor for MyPair that takes a String with both elements and parses them to the MyPair elements:
public MyPair(String both) {
String[] pairs = both.split("and");
this.first = pairs[0].trim();
this.second = pairs[1].trim();
}
And the toString() of our Map<MyPair,String> object is:
{Abbott and Costello=Comedy}
There is another option for the case when we deserialize into a Java class that contains a * Map *— we can use Jackson's * KeyDeserializer * class , one of many Deserialization classes that Jackson offers. We annotate our ClassWithAMap with @JsonCreator, @JsonProperty, and @JsonDeserialize:
public class ClassWithAMap {
@JsonProperty("map")
@JsonDeserialize(keyUsing = MyPairDeserializer.class)
private Map<MyPair, String> map;
@JsonCreator
public ClassWithAMap(Map<MyPair, String> map) {
this.map = map;
}
// public getters/setters omitted
}
We are telling Jackson to deserialize the Map<MyPair, String> _contained in _ClassWithAMap, so we need to extend KeyDeserializer _to describe how to deserialize the map's key, a _MyPair _object, from an input _String:
public class MyPairDeserializer extends KeyDeserializer {
@Override
public MyPair deserializeKey(
String key,
DeserializationContext ctxt) throws IOException,
JsonProcessingException {
return new MyPair(key);
}
}
We test the deserialization out using readValue:
String jsonInput = "{\"Abbott and Costello\":\"Comedy\"}";
ClassWithAMap classWithMap = mapper.readValue(jsonInput,
ClassWithAMap.class);
Again, the toString() method of our ClassWithAMap's map gives us the output we expect:
{Abbott and Costello=Comedy}
4.3. Map<Object,Object> Deserialization
Lastly, let's change our input JSON and the TypeReference of our destination to Map<MyPair, MyPair>:
String jsonInput = "{\"Abbott and Costello\" : \"Comedy and 1940s\"}";
TypeReference<HashMap<MyPair, MyPair>> typeRef
= new TypeReference<HashMap<MyPair, MyPair>>() {};
Map<MyPair,MyPair> map = mapper.readValue(jsonInput, typeRef);
And the toString() of our Map<MyPair, MyPair> object is:
{Abbott and Costello=Comedy and 1940s}
5. Conclusion
In this quick tutorial, we've seen how to serialize and deserialize Java Maps to and from JSON-formatted Strings.
As always, you can check out the example provided in this article in the Github repository.