Jackson List serialization
在这篇教程里,我们将看到如何把java中的list转换为JSON。序列化list有一点点棘手,因为在默认情况下,序列化以及反序列化List时类型信息并没有被存储。在这篇教程里,我们将看到两个栗子。第一个栗子里我们将一个拥有list作为其属性的对象序列化为JSON。在第二个栗子里,我们将尝试直接序列化List。在这两个栗子里,我们都将使用特殊的配置以保存类型信息;
栗子一:序列化一个包含List的对象
这个栗子将Zoo转换为JSON,Zoo类包含名字,城市以及一个动物列表。列表将包含抽象父类Animal的子类作为元素。让我们看看当序列化Zoo的时候会发生什么。首先我们将创建Zoo类。注意它的构造函数。当我们尝试着从JSON里得到Zoo对象是,Jackson必须知道要使用的是包含名字以及城市属性的构造函数。
public class Zoo {
public String name;
public String city;
public List<Animal> animals = new ArrayList<>();
@JsonCreator
public Zoo(@JsonProperty("name") String name, @JsonProperty("city") String city) {
this.name = name;
this.city = city;
}
public List<Animal> addAnimal(Animal animal) {
animals.add(animal);
return animals;
}
}
Animal是一个抽象类。它被进一步扩展以创建Elephant和Lion类
/**
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include=JsonTypeInfo.As.PROPERTY, property = "@class")
@JsonSubTypes({ @Type(value = Lion.class, name = "lion"), @Type(value = Elephant.class, name = "elephant") })
首先注释掉这些注解
**/
public abstract class Animal {
@JsonProperty("name")
String name;
@JsonProperty("sound")
String sound;
@JsonProperty("type")
String type;
@JsonProperty("endangered")
boolean endangered;
}
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;
}
}
接下来让我们为Zoo创建一个序列化器,设想一下伦敦动物园需要搬到一个更大的地方,我们需要将zoo从它当前的地方序列化,然后在一个更大的地方反序列化;
我们使用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);
}
}
//输出
{
"name" : "London Zoo",
"city" : "London",
"animals" : [ {
"name" : "Manny",
"sound" : null,
"type" : null,
"endangered" : false
}, {
"name" : "Simba",
"sound" : null,
"type" : null,
"endangered" : false
} ]
}
好了,让我们看看我们的动物园。需要记住的是,当我们反序列化时,我们还需要重新生成我们的动物园(包括它拥有的动物);
Manny 和Simba在这里,但是在JSON里没有任何信息表明Manny是一头大象,Simba是一头狮子。让我们看看当我们反序列化时会发生什么。
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);
}
}
我们遇到了一个错误:“Exception in thread “main” com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of com.xiaomo.util.json.example.list_serialization.Animal
(no Creators, like default construct, exist): abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information ”。好吧,其实我们正在期待这个。让我们看看如何解决它。我们需要将类型信息放到JSON里。这里有两件你需要做的事:
-
告诉Jackson,我们需要为Animal类包含类型信息;
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = As.PROPERTY, property = "@class")
-
告诉Jackson,Animal有Elephant和Lion子类;
@JsonSubTypes({ @Type(value = Lion.class, name = "lion"), @Type(value = Elephant.class, name = "elephant") })
接下来,我们看看序列化的结果(译者注:这样才能反序列化呀):
{"name":"London Zoo","city":"London",
"animals":[{"@class":"com.studytrails.json.jackson.Elephant","name":"Manny"},
{"@class":"com.studytrails.json.jackson.Lion","name":"Simba"}]}
看起来好多了。现在如果你将JSON再转换为Zoo,就可以成功了(不会丢失动物);
栗子二:序列化一个List
在上一个栗子中,我们看到了如何序列化一个拥有List属性的对象。在这个栗子中,我们将看看如何直接序列化一个List。我们将使用相同的类。让我们看看当我们尝试直接序列化List时会发生什么(注意我们已经在Animal类上添加了关于类型的注释);
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);
}
//输出
[ {
"name" : "Samba",
"sound" : null,
"type" : null,
"endangered" : false
}, {
"name" : "Manny",
"sound" : null,
"type" : null,
"endangered" : false
} ]
没有类型信息!为了在直接序列化List时添加类型信息,你需要这样配置你的mapper:
mapper.writerWithType(new TypeReference<List<Animal>>(){})
.writeValue(System.out, animals);//译者注:最新Jackson里writerWithType已被废弃,可使用writerFor函数);
//输出
[{"@class":"com.studytrails.json.jackson.Lion","name":"Samba"},
{"@class":"com.studytrails.json.jackson.Elephant","name":"Manny"}]
mapper.writerFor(new TypeReference<List<Animal>>(){})
.writeValue(System.out, animals);
总结一下,在这篇教程里,我们看到了如何序列化一个包含List的Java对象以及直接序列化一个List。
项目源码免费获取 个人博客,百度网盘地址+提取码,文件无密~