In this tutorial we will see how to convert a java List to JSON. Serializing list is a little tricky since by default the type info is not stored while serializing and deserializing lists. In this tutorial we look at two examples. In the first example we serialize an Object that has a java List as one of its properties. In the second example we try and serialize the List directly. In both examples we use special configuration to preserve type info.
The example converts a Zoo class to json. the zoo class contains the name of zoo, its city and a list of animals. The list is of type ‘Animal’, i.e. the list contains elements that are subclass of the Abstract class Animal. Lets see what happens when we try to serialize zoo. First we create the Zoo class. Notice how the constructor looks. When we try to get the Zoo Object back from the JSON, Jackson has to know that it should create the Zoo Object using the constructor that takes in the name and city properties.
class Zoo {
public String name;
public String city;
@JsonCreator
public Zoo(@JsonProperty("name") String name,@JsonProperty("city") String city) {
this.name = name;
this.city = city;
}
public List<Animal> animals = new ArrayList<Animal>();
public List<Animal> addAnimal(Animal animal) {
animals.add(animal);
return animals;
}
}
The Animal is an abstract class. It is further extended to create the Elephant and the Lion class.
class Elephant extends Animal {
@JsonCreator
public Elephant(@JsonProperty("name") String name) {
super.name = name;
}
@Override
public String toString() {
return "Elephant : " + name;
}
}
class Lion extends Animal {
@JsonCreator
public Lion(@JsonProperty("name") String name) {
this.name = name;
}
@Override
public String toString() {
return "Lion: " + name;
}
}
Lets now create a serializer for Zoo. Imagine that the London zoo needs to shift to a larger area. We will have to serialize the zoo from its current location and deserialize it at the target location. (a teleportation machine)
We use the ObjectMapper class to do the serialization and print the json to console. You could also print it to a file
package com.studytrails.json.jackson;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
public class SerializeZoo {
public static void main(String[] args) throws JsonGenerationException, JsonMappingException, IOException {
Zoo zoo = new Zoo("London Zoo", "London");
Lion lion = new Lion("Simba");
Elephant elephant = new Elephant("Manny");
zoo.addAnimal(elephant).add(lion);
ObjectMapper mapper = new ObjectMapper();
mapper.writeValue(System.out, zoo);
}
}
Ok so lets see how our zoo looks like. Remember, we should be able to re create the zoo (along with all its animals) when we deserialize it
{"name":"London Zoo","city":"London","animals":[{"name":"Manny"},{"name":"Simba"}]}
Clearly our teleporter is not ready. Manny and Simba are in there, but there is nothing in the json that says that Manny is an elephant and Simba is a lion. Lets see what happens when we try to deserialize the zoo
package com.studytrails.json.jackson;
import java.io.IOException;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
public class DeserializeZoo {
public static void main(String[] args) throws JsonParseException, JsonMappingException, IOException {
String json = "{\"name\":\"London Zoo\",\"city\":\"London\"," + "\"animals\":[{\"name\":\"Manny\"},{\"name\":\"Simba\"}]}";
ObjectMapper mapper = new ObjectMapper();
mapper.readValue(json, Zoo.class);
}
}
We get an error! the error says “Can not construct instance of com.studytrails.json.jackson.Animal, problem: abstract types either need to be mapped to concrete types, have custom deserializer, or be instantiated with additional type information” . Ok, we were expecting this. Lets see how to resolve it. We need to be able to put the information about the type in the json. There are two things you need to do this
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = As.PROPERTY, property = "@class")
@JsonSubTypes({ @Type(value = Lion.class, name = "lion"), @Type(value = Elephant.class, name = "elephant") })
Lets see how our json looks now
{"name":"London Zoo","city":"London",
"animals":[{"@class":"com.studytrails.json.jackson.Elephant","name":"Manny"},
{"@class":"com.studytrails.json.jackson.Lion","name":"Simba"}]}
This looks much better. Now, if you read the json back using the Zoo class then it works well and the Zoo class would have a list of animals.
In the previous example we saw how to serialize a class that has a List. In this example lets see how to serialize a list directly. We will use the same Animal, Lion and Elephant classes. Lets see what happens when we try to serialize the list directly (note that we have added the typeinfo annotations to the Animal class)
public class SerializeAnimals {
public static void main(String[] args) throws JsonGenerationException, JsonMappingException, IOException {
List<animal> animals = new ArrayList<animal>();
Lion lion = new Lion("Samba");
Elephant elephant = new Elephant("Manny");
animals.add(lion);
animals.add(elephant);
ObjectMapper mapper = new ObjectMapper();
mapper.writeValue(System.out, animals);
}
}
</animal></animal>
This gives the following json
[{"name":"Samba"},{"name":"Manny"}]
No type info! To add type info while serializing Lists directly you need to configure the mapper in this way
mapper.writerWithType(new TypeReference<List
<Animal>>() {
}).writeValue(System.out, animals);
This now produces the correct json
[{"@class":"com.studytrails.json.jackson.Lion","name":"Samba"},
{"@class":"com.studytrails.json.jackson.Elephant","name":"Manny"}]
So to recap, in this tutorial we saw how to i. serialize a Java object containing a List and ii. serialize a list (root object)
(Note: This article’s original links is here )