JSON Jackson - 使用自定义序列化程序序列化多态类时的异常

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JSON Jackson - 使用自定义序列化程序序列化多态类时的异常相关的知识,希望对你有一定的参考价值。

我目前正在将一些代码从Jackson 1.x迁移到Jackson 2.5 json mapper,这是一个长期存在的问题,而不是1.x.

这是设置(见下面的代码):

  • 接口IPet
  • class Dog实现了IPet
  • IPet使用@JsonTypeInfo和@JsonSubTypes进行注释
  • class Human有一个IPet类型的属性,用@JsonSerialize注释(using = CustomPetSerializer.class)

问题:如果我序列化Dog的一个实例,它按预期工作(杰克逊也将类型信息添加到json字符串)。但是当我序列化Human类的一个实例时,会抛出一个异常:

com.fasterxml.jackson.databind.JsonMappingException:未为类型com.pet.Dog实现的类型id处理(通过引用链:com.Human [“pet”])

CustomPetSerializer类的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""));
}
答案

你需要在你的serializeWithType中额外覆盖CustomPetSerializer,因为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);
}

这将为你的{"pet":{"type":"dog":{"age":"7"}}}实例打印Human

另一答案

因为杰克逊2.9 writeTypePrefixForObject()writeTypeSuffixForObject()已被弃用(我不清楚为什么)。现在似乎在新的方法下:

@Override
public void serializeWithType(IPet value, JsonGenerator gen, 
        SerializerProvider provider, TypeSerializer typeSer) 
        throws IOException, JsonProcessingException {

  WritableTypeId typeId = typeSerializer.typeId(value, START_OBJECT);
  typeSer.writeTypePrefix(gen, typeId);
  serialize(value, gen, provider); // call your customized serialize method
  typeSer.writeTypeSuffix(gen, typeId);
}

所以现在是一条额外的线,所以不确定为什么它向前迈进了一步,也许它更有效地重用typeId对象。

资料来源:Jackson目前掌握的ObjectNode课程。不是最好的来源,但没有看到任何升级文档解释该怎么做。

以上是关于JSON Jackson - 使用自定义序列化程序序列化多态类时的异常的主要内容,如果未能解决你的问题,请参考以下文章

在 Jackson / Spring Boot 中测试自定义 Json Deserializer

json之jackson的常用全局配置以及自定义序列化规则

Jackson:为 Map 数据结构注册自定义 XML 序列化程序

Jackson:如何在不修改 POJO 的情况下向 JSON 添加自定义属性

Java Jackson如何在自定义序列化程序中为对象使用默认序列化程序

Spring Boot - 自定义 JSON 序列化 [重复]