JSON Jackson - 使用自定义序列化器序列化多态 class 时出现异常
JSON Jackson - exception when serializing a polymorphic class with custom serializer
我目前正在将一些代码从 Jackson 1.x 迁移到 Jackson 2.5 json 映射器,但遇到了一个 1.x 中没有的问题。
这是设置(见下面的代码):
- IPet 界面
- class狗实现IPet
- IPet 使用@JsonTypeInfo 和@JsonSubTypes 注释
- class Human 有一个 属性 类型的 IPet,用 @JsonSerialize(using=CustomPetSerializer.class)
注释
问题:
如果我序列化 Dog 的实例,它会按预期工作(类型信息也由 Jackson 添加到 json 字符串)。
但是,当我序列化 Human class 的实例时,会抛出一个异常:
com.fasterxml.jackson.databind.JsonMappingException: Type id handling
not implemented for type com.pet.Dog (through reference chain:
com.Human["pet"])
未调用 CustomPetSerializer class 的 serialize(...) 方法(使用断点进行测试)。
代码:
IPet 实现:
@JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.PROPERTY, property="type")
@JsonSubTypes({
@JsonSubTypes.Type(value=Dog.class, name="dog")
//,@JsonSubTypes.Type(value=Cat.class, name="cat")
//more subtypes here...
})
public interface IPet
{
public Long getId();
public String getPetMakes();
}
狗实现:
public class Dog implements IPet
{
@Override
public String getPetMakes()
{
return "Wuff!";
}
@Override
public Long getId()
{
return 777L;
}
}
养狗的人:
public class Human
{
private IPet pet = new Dog();
@JsonSerialize(using=CustomPetSerializer.class)
public IPet getPet()
{
return pet;
}
}
CustomPetSerializer 实现:
public class CustomPetSerializer extends JsonSerializer<IPet>
{
@Override
public void serialize(IPet value, JsonGenerator gen, SerializerProvider serializers) throws IOException, JsonProcessingException
{
if(value != null && value.getId() != null)
{
Map<String,Object> style = new HashMap<String,Object>();
style.put("age", "7");
gen.writeObject(style);
}
}
}
JUnit测试方法:
@Test
public void testPet() throws JsonProcessingException
{
ObjectMapper mapper = new ObjectMapper();
Human human = new Human();
//works as expcected
String json = mapper.writeValueAsString(human.getPet());
Assert.assertNotNull(json);
Assert.assertTrue(json.equals("{\"type\":\"dog\",\"id\":777,\"petMakes\":\"Wuff!\"}"));
//throws exception: Type id handling not implemented for type com.pet.Dog (through reference chain: com.Human["pet"])
json = mapper.writeValueAsString(human); //exception is thrown here
Assert.assertNotNull(json);
Assert.assertTrue(json.contains("\"age\":\"7\""));
}
您还需要在 CustomPetSerializer
中覆盖 serializeWithType
,因为 IPet
是多态的。这也是 serialize
没有被调用的原因。检查 this related SO question 详细解释了何时调用 serializeWithType
。例如,您的 serializeWithType
实现可能如下所示:
@Override
public void serializeWithType(IPet value, JsonGenerator gen,
SerializerProvider provider, TypeSerializer typeSer)
throws IOException, JsonProcessingException {
typeSer.writeTypePrefixForObject(value, gen);
serialize(value, gen, provider); // call your customized serialize method
typeSer.writeTypeSuffixForObject(value, gen);
}
这将为您的 Human
实例打印 {"pet":{"type":"dog":{"age":"7"}}}
。
从 Jackson 2.9 开始,writeTypePrefixForObject()
和 writeTypeSuffixForObject()
已被弃用(我不清楚为什么)。在新方法下,它现在似乎是:
@Override
public void serializeWithType(IPet value, JsonGenerator gen,
SerializerProvider provider, TypeSerializer typeSer)
throws IOException, JsonProcessingException {
WritableTypeId typeId = typeSer.typeId(value, START_OBJECT);
typeSer.writeTypePrefix(gen, typeId);
serialize(value, gen, provider); // call your customized serialize method
typeSer.writeTypeSuffix(gen, typeId);
}
所以现在多了一行,所以不确定为什么它向前迈出了一步,也许它更有效地重用 typeId
对象。
来源: Jackson 的 ObjectNode class 目前在读硕士。不是最好的来源,但看不到任何升级文档来解释该怎么做。
我目前正在将一些代码从 Jackson 1.x 迁移到 Jackson 2.5 json 映射器,但遇到了一个 1.x 中没有的问题。
这是设置(见下面的代码):
- IPet 界面
- class狗实现IPet
- IPet 使用@JsonTypeInfo 和@JsonSubTypes 注释
- class Human 有一个 属性 类型的 IPet,用 @JsonSerialize(using=CustomPetSerializer.class) 注释
问题: 如果我序列化 Dog 的实例,它会按预期工作(类型信息也由 Jackson 添加到 json 字符串)。 但是,当我序列化 Human class 的实例时,会抛出一个异常:
com.fasterxml.jackson.databind.JsonMappingException: Type id handling not implemented for type com.pet.Dog (through reference chain: com.Human["pet"])
未调用 CustomPetSerializer class 的 serialize(...) 方法(使用断点进行测试)。
代码:
IPet 实现:
@JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.PROPERTY, property="type")
@JsonSubTypes({
@JsonSubTypes.Type(value=Dog.class, name="dog")
//,@JsonSubTypes.Type(value=Cat.class, name="cat")
//more subtypes here...
})
public interface IPet
{
public Long getId();
public String getPetMakes();
}
狗实现:
public class Dog implements IPet
{
@Override
public String getPetMakes()
{
return "Wuff!";
}
@Override
public Long getId()
{
return 777L;
}
}
养狗的人:
public class Human
{
private IPet pet = new Dog();
@JsonSerialize(using=CustomPetSerializer.class)
public IPet getPet()
{
return pet;
}
}
CustomPetSerializer 实现:
public class CustomPetSerializer extends JsonSerializer<IPet>
{
@Override
public void serialize(IPet value, JsonGenerator gen, SerializerProvider serializers) throws IOException, JsonProcessingException
{
if(value != null && value.getId() != null)
{
Map<String,Object> style = new HashMap<String,Object>();
style.put("age", "7");
gen.writeObject(style);
}
}
}
JUnit测试方法:
@Test
public void testPet() throws JsonProcessingException
{
ObjectMapper mapper = new ObjectMapper();
Human human = new Human();
//works as expcected
String json = mapper.writeValueAsString(human.getPet());
Assert.assertNotNull(json);
Assert.assertTrue(json.equals("{\"type\":\"dog\",\"id\":777,\"petMakes\":\"Wuff!\"}"));
//throws exception: Type id handling not implemented for type com.pet.Dog (through reference chain: com.Human["pet"])
json = mapper.writeValueAsString(human); //exception is thrown here
Assert.assertNotNull(json);
Assert.assertTrue(json.contains("\"age\":\"7\""));
}
您还需要在 CustomPetSerializer
中覆盖 serializeWithType
,因为 IPet
是多态的。这也是 serialize
没有被调用的原因。检查 this related SO question 详细解释了何时调用 serializeWithType
。例如,您的 serializeWithType
实现可能如下所示:
@Override
public void serializeWithType(IPet value, JsonGenerator gen,
SerializerProvider provider, TypeSerializer typeSer)
throws IOException, JsonProcessingException {
typeSer.writeTypePrefixForObject(value, gen);
serialize(value, gen, provider); // call your customized serialize method
typeSer.writeTypeSuffixForObject(value, gen);
}
这将为您的 Human
实例打印 {"pet":{"type":"dog":{"age":"7"}}}
。
从 Jackson 2.9 开始,writeTypePrefixForObject()
和 writeTypeSuffixForObject()
已被弃用(我不清楚为什么)。在新方法下,它现在似乎是:
@Override
public void serializeWithType(IPet value, JsonGenerator gen,
SerializerProvider provider, TypeSerializer typeSer)
throws IOException, JsonProcessingException {
WritableTypeId typeId = typeSer.typeId(value, START_OBJECT);
typeSer.writeTypePrefix(gen, typeId);
serialize(value, gen, provider); // call your customized serialize method
typeSer.writeTypeSuffix(gen, typeId);
}
所以现在多了一行,所以不确定为什么它向前迈出了一步,也许它更有效地重用 typeId
对象。
来源: Jackson 的 ObjectNode class 目前在读硕士。不是最好的来源,但看不到任何升级文档来解释该怎么做。