Jackson deserialize java map

Map Serialization and Deserialization with Jackson

If you have a few years of experience in the Java ecosystem, and you’re interested in sharing that experience with the community (and getting paid for your work of course), have a look at the «Write for Us» page. Cheers, Eugen

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 , Map, and Map to and from JSON-formatted Strings.

Further reading:

Jackson – Working with Maps and nulls

How To Serialize and Deserialize Enums with Jackson

XML Serialization and Deserialization with Jackson

This short tutorial shows how the Jackson library can be used to serialize Java object to XML and deserialize them back to objects.

2. Maven Configuration

 com.fasterxml.jackson.core jackson-databind 2.11.1 

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 Serialization

For the simple case, let’s create a Map and serialize it to JSON:

Map 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:

3.2. Map 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  < 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 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 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:

3.3. Map Serialization

The most complex case is serializing a Map , 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 map; @JsonSerialize(keyUsing = MyPairSerializer.class) MyPair mapKey; @JsonSerialize(keyUsing = MyPairSerializer.class) MyPair mapValue;

Let’s test out serializing our Map :

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:

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 Deserialization

For the simple case, let’s take a JSON-formatted input string and convert it to a Map Java collection:

String jsonInput = ""; TypeReference> typeRef = new TypeReference>() <>; Map 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:

4.2. Map Deserialization

Now, let’s change our input JSON and the TypeReference of our destination to Map :

String jsonInput = ""; TypeReference> typeRef = new TypeReference>() <>; Map 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:

And the toString() of our Map object is:

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 Mapmap; @JsonCreator public ClassWithAMap(Map map) < this.map = map; >// public getters/setters omitted >

We are telling Jackson to deserialize the Map 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 = ""; ClassWithAMap classWithMap = mapper.readValue(jsonInput, ClassWithAMap.class);

Again, the toString() method of our ClassWithAMap’s map gives us the output we expect:

4.3. Map Deserialization

Lastly, let’s change our input JSON and the TypeReference of our destination to Map :

String jsonInput = ""; TypeReference> typeRef = new TypeReference>() <>; Map map = mapper.readValue(jsonInput, typeRef);

And the toString() of our Map object is:

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.

Источник

How to Deserialize a Map correctly in Jackson

If Jackson gets an object to serialize, it tries to serialize all of its values. And only its values (which is pretty good for the independence from classes). This is a json object:

Now, what is the class of this object? It could be Fruit.class , Image.class or even RoundObject.class , json doesn’t know and Jackson neither.

So how does json find out what the class is? By looking at the type of the object reference. In your case, it’s Object. In Object.class, Jackson cannot find a constructor that requires the variables of the object that has been saved, so it crashes.

Trying to serialize objects is not a good idea. If you have very different classes you want to put in, e.g. Apple and Banana , make an interface or abstract class called Fruit that both of them implement. Now, use this annotation at the top of this class:

@JsonTypeInfo( use = JsonTypeInfo.Id.NAME, property = "type") // name of the variable to save the kind of object you put in. NO VARIABLES in all classes that extend from Fruit are allowed to have this name (or at least @JsonProperty). @JsonSubTypes(< @JsonSubTypes.Type(value = Apple.class, name = "banana"), @JsonSubTypes.Type(value = Banana.class, name = "apple"), >) 

And using a Map should work.

I ended up using custom deserialization to achieve the desired result, see my answer. stackoverflow.com/a/68133685/1386613

The solution that worked for me was using custom deserialization, @JsonDeserialize annotation & JsonDeserializer interface, in order to achieve the desired results.

public class Request < private String method; private String resource; @JsonDeserialize(using = BodyDeserializer.class) private final Mapbody; private final Map headers; private final Map parameters; public Request() < this.body = new HashMap(); this.headers = new HashMap(); this.parameters = new HashMap(); >public String getMethod() < return method; >public String getResource() < return resource; >public Map getBody() < return body; >public Map getHeaders() < return headers; >public Map getParameters() < return parameters; >public String getHeader(String name) < return headers.get(name); >public Request setBody(Map body) < this.body.putAll(body); return this; >public Request setMethod(String name) < this.method = name; return this; >public Request setResource(String name) < this.resource = name; return this; >public Request setHeaders(Map headers) < this.headers.putAll(headers); return this; >public Request setParameters(Map parameters) < this.parameters.putAll(parameters); return this; >private static class BodyDeserializer extends JsonDeserializer < @Override public Mapdeserialize(JsonParser jp, DeserializationContext dc) throws IOException, JsonProcessingException < JsonDeserializerdeserializer = dc.findRootValueDeserializer(dc.constructType(Map.class)); Map map = (Map) deserializer.deserialize(jp, dc); return map; > > > 
Message actual = createMessage(); String json = JsonUtils.prettyPrint().writeValue(actual); System.out.println(json); Message expected = JsonUtils.readValue(json, Message.class); 
public class MavenMain < public static void main(String. args) < Message actual = createMessage(); String json = JsonUtils.prettyPrint().writeValue(actual); System.out.println(json); Message expected = JsonUtils.readValue(json, Message.class); >private static Message createMessage() < Message message = new Message(); message.setData(createData()); message.setRequest(createRequest()); message.setResponse(createResponse()); return message; >private static Data createData() < Mapwhatsapp = new LinkedHashMap<>(); whatsapp.put("provider", "clickatell"); whatsapp.put("name", "Dave"); whatsapp.put("destination", "123456789098"); whatsapp.put("source", "123456789098"); whatsapp.put("message", "Your payment of $1.00 received, your receipt.no is QWJ124XPA9."); Map cashapp = new LinkedHashMap<>(); cashapp.put("receiptNo", "QWJ124XPA9"); cashapp.put("name", "Dave Chapelle"); cashapp.put("msisdn", "123456789098"); Map dataMap = new LinkedHashMap<>(); dataMap.put("whatsapp", whatsapp); dataMap.put("cashapp", cashapp); Data data = new Data(); data.setDataMap(dataMap); return data; > private static Request createRequest() < Mapwhatsapp = new LinkedHashMap<>(); whatsapp.put("conversationId", "39f09c41-1bd3-4e81-b829-babed3747d4b"); whatsapp.put("name", "Dave"); whatsapp.put("source", "+123456789098"); Map payment = new LinkedHashMap<>(); payment.put("product", "chocolate"); payment.put("amount", 1); payment.put("method", "cashapp"); payment.put("msisdn", "123456789098"); payment.put("entity", "The Fudge Shop"); Map body = new HashMap<>(); body.put("whatsapp", whatsapp); body.put("payment", payment); Request request = new Request(); request.setHeaders(Collections.emptyMap()); request.setMethod(""); request.setResource(""); request.setBody(body); request.setParameters(Collections.emptyMap()); return request; > private static Response createResponse() < Response response = new Response(); response.setCode("202"); response.setData(""); response.setMessageId("20210623160202a647d32ee9ae477f9c90d8b1fbfd763a"); response.setDescription("Processing Request"); response.setTimestamp("2021-06-23T16:02:02.408"); return response; >> class Message < private Data data; private Request request; private Response response; public void setData(Data data) < this.data = data; >public void setRequest(Request request) < this.request = request; >public void setResponse(Response response) < this.response = response; >> class Data < @JsonProperty("sets") private Map dataMap; public void setDataMap(Map dataMap) < this.dataMap = dataMap; >> class Request < private String method; private String resource; private Mapbody; private Map headers; private Map parameters; public void setMethod(String method) < this.method = method; >public void setResource(String resource) < this.resource = resource; >public void setBody(Map body) < this.body = body; >public void setHeaders(Map headers) < this.headers = headers; >public void setParameters(Map parameters) < this.parameters = parameters; >> class Response < private String code; private String data; private String messageId; private String timestamp; private String description; public void setCode(String code) < this.code = code; >public void setData(String data) < this.data = data; >public void setMessageId(String messageId) < this.messageId = messageId; >public void setTimestamp(String timestamp) < this.timestamp = timestamp; >public void setDescription(String description) < this.description = description; >> 

In case you want to use immutable object, then it’s a bit another configuration of models, but code in the main class will be the same.

Источник

Jackson: Deserialize to a Map with correct type for each value

I’m looking to be able to make the round trip with Jackson from object to JSON and back. I can serialize the object above fine and receive the following output:

The issue I’m running into is that since the values in the serialized map do not have any type information, they are not deserialized correctly. For example, in the sample above, enumValue1 should be deserialized as an enum value but is instead deserialized as a String. I’ve seen examples for basing what type on a variety of things, but in my scenario, I won’t know what the types are (they will be user generated objects that I won’t know in advance) so I need to be able to serialize the type information with the key value pair. How can I accomplish this with Jackson? For the record, I’m using Jackson version 2.4.2. The code I’m using to test the round trip is as follows:

@Test @SuppressWarnings("unchecked") public void testJsonSerialization() throws Exception < // Get test object to serialize T serializationValue = getSerializationValue(); // Serialize test object String json = mapper.writeValueAsString(serializationValue); // Test that object was serialized as expected assertJson(json); // Deserialize to complete round trip T roundTrip = (T) mapper.readValue(json, serializationValue.getClass()); // Validate that the deserialized object matches the original one assertObject(roundTrip); >
@Configuration public static class SerializationConfiguration < @Bean public ObjectMapper mapper() < Map, Class> mixins = new HashMap, Class>(); // Add unrelated MixIns .. return new Jackson2ObjectMapperBuilder() .featuresToDisable(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS) .dateFormat(new ISO8601DateFormatWithMilliSeconds()) .mixIns(mixins) .build(); > > 

Источник

Читайте также:  Линейный конгруэнтный метод python
Оцените статью