我在使用自定义序列化器时遇到了Jackson的问题,它不尊重@JsonTypeInfo注释。下面的简化示例不需要自定义序列化,并在不使用自定义序列化器时按预期输出"type"属性。但是,一旦启用了自定义序列化器,"type"属性就不会被写入,从而阻止反序列化。这是我的实际系统的测试用例,该系统需要自定义序列化器,并且如果有区别的话,正在尝试序列化Map>,其中T是多态类,其类型信息未被写入。我该如何编写自定义序列化器以正确处理类型信息?我希望如果我可以让下面的测试用例正常工作,我将能够将相同的概念应用于实际代码。
测试程序尽可能地模拟了我在真实应用程序中使用的序列化过程,构建了一个Map<>并对其进行序列化,而不是直接对Zoo进行序列化。
预期输出:
测试程序尽可能地模拟了我在真实应用程序中使用的序列化过程,构建了一个Map<>并对其进行序列化,而不是直接对Zoo进行序列化。
预期输出:
{
"Spike": {
"type": "dog",
"name": "Spike",
"breed": "mutt",
"leashColor": "red"
},
"Fluffy": {
"type": "cat",
"name": "Fluffy",
"favoriteToy": "spider ring"
}
}
通过在SimpleModule
中注释自定义序列化程序的注册,您可以看到输出类型信息,但是在注册了序列化程序的情况下,输出与上面的输出相同,但没有type
属性。
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.codehaus.jackson.JsonGenerator;
import org.codehaus.jackson.JsonProcessingException;
import org.codehaus.jackson.Version;
import org.codehaus.jackson.annotate.JsonCreator;
import org.codehaus.jackson.annotate.JsonProperty;
import org.codehaus.jackson.annotate.JsonSubTypes;
import org.codehaus.jackson.annotate.JsonSubTypes.Type;
import org.codehaus.jackson.annotate.JsonTypeInfo;
import org.codehaus.jackson.annotate.JsonTypeInfo.As;
import org.codehaus.jackson.annotate.JsonTypeInfo.Id;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.JsonSerializer;
import org.codehaus.jackson.map.Module;
import org.codehaus.jackson.map.Module.SetupContext;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.ResolvableSerializer;
import org.codehaus.jackson.map.SerializationConfig.Feature;
import org.codehaus.jackson.map.SerializerProvider;
import org.codehaus.jackson.map.module.SimpleModule;
import org.codehaus.jackson.map.module.SimpleSerializers;
public class JacksonTest {
public static void main(String[] args) throws Exception {
ObjectMapper mapper = new ObjectMapper();
Module m = new SimpleModule("TestModule", new Version(1,0,0,"")) {
@Override
public void setupModule(SetupContext context) {
super.setupModule(context);
context.setMixInAnnotations(Animal.class, AnimalMixIn.class);
SimpleSerializers serializers = new SimpleSerializers();
serializers.addSerializer(Zoo.class, new ZooSerializer());
context.addSerializers(serializers);
}
};
mapper.registerModule(m);
mapper.configure(Feature.INDENT_OUTPUT, true);
Zoo zoo = new Zoo();
List<Animal> animals = new ArrayList<Animal>();
animals.add(new Dog("Spike", "mutt", "red"));
animals.add(new Cat("Fluffy", "spider ring"));
zoo.animals = animals;
System.out.println(zoo);
String json = mapper.writeValueAsString(zoo);
System.out.println(json);
}
static class Zoo {
public Collection<Animal> animals = Collections.EMPTY_SET;
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("Zoo: { ");
for (Animal animal : animals)
sb.append(animal.toString()).append(" , ");
return sb.toString();
}
}
static class ZooSerializer extends JsonSerializer<Zoo> {
@Override
public void serialize(Zoo t, JsonGenerator jg, SerializerProvider sp) throws IOException, JsonProcessingException {
Map<Object, Animal> animalMap = new HashMap<Object, Animal>();
for (Animal a : t.animals)
animalMap.put(a.getName(), a);
jg.writeObject(animalMap);
}
}
@JsonTypeInfo(
use=Id.NAME,
include=As.PROPERTY,
property="type")
@JsonSubTypes({
@Type(value=Cat.class,name="cat"),
@Type(value=Dog.class,name="dog")
})
static abstract class AnimalMixIn {
}
static interface Animal<T> {
T getName();
}
static abstract class AbstractAnimal<T> implements Animal<T> {
private final T name;
protected AbstractAnimal(T name) {
this.name = name;
}
public T getName() {
return name;
}
}
static class Dog extends AbstractAnimal<String> {
private final String breed;
private final String leashColor;
@JsonCreator
public Dog(@JsonProperty("name") String name, @JsonProperty("breed") String breed,
@JsonProperty("leashColor") String leashColor)
{
super(name);
this.breed = breed;
this.leashColor = leashColor;
}
public String getBreed() {
return breed;
}
public String getLeashColor() {
return leashColor;
}
@Override
public String toString() {
return "Dog{" + "name=" + getName() + ", breed=" + breed + ", leashColor=" + leashColor + "}";
}
}
static class Cat extends AbstractAnimal<String> {
private final String favoriteToy;
@JsonCreator
public Cat(@JsonProperty("name") String name, @JsonProperty("favoriteToy") String favoriteToy) {
super(name);
this.favoriteToy = favoriteToy;
}
public String getFavoriteToy() {
return favoriteToy;
}
@Override
public String toString() {
return "Cat{" + "name=" + getName() + ", favoriteToy=" + favoriteToy + '}';
}
}
}
编辑:添加可能澄清问题的其他测试用例
阅读了一些关于类似问题的更多问题后,我决定尝试修改我的自定义序列化程序以缩小问题所在的范围。我发现将我的Animal
对象添加到任何通用类型的集合中(已测试过List<Animal>
和Map<Object, Animal>
),类型信息未被序列化。然而,当序列化一个Animal[]
时,类型信息被包含。不幸的是,虽然我可以在测试代码中改变这种行为,但我需要生产代码来序列化一个具有多态值的Map
。
将自定义ZooSerializer.serialize()
方法更改为以下内容会输出类型信息,但会失去我需要的Map
语义:
public void serialize(...) {
Animal[] animals = t.animals.toArray(new Animal[0]);
jg.writeObject(animals);
}
JsonGenerator
使用AnimalMixIn
配置似乎是合理的。希望StaxMan能够提供意见。 - Programmer Bruce