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里。这里有两件你需要做的事:

  1. 告诉Jackson,我们需要为Animal类包含类型信息;
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = As.PROPERTY, property = "@class")
  1. 告诉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。