You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Jackson无法创建已注册类实例求助:@JsonIdentityInfo致反序列化失败

嘿,我之前也踩过Jackson这个序列化引用的坑!结合你说的场景和可复现示例,咱们来好好聊聊这个问题:

问题分析与解决方案

首先先把你提到的可复现示例补全(方便大家理解场景):

示例代码

子类 Child(带@JsonIdentityInfo注解)

import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;

@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
public class Child {
    private Long id;
    private String name;

    // 空构造器(Jackson反序列化必需)
    public Child() {}
    public Child(Long id, String name) {
        this.id = id;
        this.name = name;
    }

    // 省略getter、setter方法
}

父类 Parent

import java.util.ArrayList;
import java.util.List;

public class Parent {
    private String parentName;
    private List<Child> children = new ArrayList<>();

    public Parent() {}
    public Parent(String parentName) {
        this.parentName = parentName;
    }

    public void addChild(Child child) {
        this.children.add(child);
    }

    // 省略getter、setter方法
}

测试代码(复现错误)

import com.fasterxml.jackson.databind.ObjectMapper;

public class JacksonReferenceTest {
    public static void main(String[] args) throws Exception {
        ObjectMapper mapper = new ObjectMapper();

        // 创建同一个子对象,重复添加到父对象的集合中
        Child repeatedChild = new Child(1L, "MyChild");
        Parent parent = new Parent("MyParent");
        parent.addChild(repeatedChild);
        parent.addChild(repeatedChild);

        // 序列化正常,输出会是:{"parentName":"MyParent","children":[{"id":1,"name":"MyChild"},1]}
        String serializedJson = mapper.writeValueAsString(parent);
        System.out.println("序列化结果:" + serializedJson);

        // 这里反序列化会抛出异常!
        Parent deserializedParent = mapper.readValue(serializedJson, Parent.class);
    }
}

问题根源

你猜的没错,这确实是Jackson 2.9.x版本的已知Bug!当使用@JsonIdentityInfo结合PropertyGenerator(依赖对象自身的ID属性)时,Jackson在反序列化集合中的重复ID引用时,无法正确关联到之前已经反序列化的子对象,导致解析失败。而移除注解后,Jackson会把每个ID对应的内容当成新对象来反序列化(即使是同一个对象的引用),所以不会报错,但这就失去了你原本想要的“只序列化一次子对象”的效果。

可行的解决方案

1. 升级Jackson版本(最推荐)

这个Bug在Jackson 2.10.0及以上版本已经被官方修复了,只要把你的Jackson依赖升级到2.10+,不需要修改任何代码,就能正常完成序列化和反序列化。

2. 更换ID生成器(无法升级版本时用)

如果暂时没办法升级版本,可以把@JsonIdentityInfo的生成器换成IntSequenceGenerator,让Jackson自动生成序列ID,而不是依赖对象自身的id属性,在2.9.x版本中这种方式的反序列化兼容性更好:

@JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class, property = "@id")
public class Child {
    // 原有代码不变
}

序列化后的JSON会变成:{"parentName":"MyParent","children":[{"@id":1,"id":1,"name":"MyChild"},1]},这样反序列化就能正常识别引用了。

3. 自定义序列化/反序列化器(最繁琐)

如果以上两种方式都不适用,可以自己写自定义序列化器,手动控制子对象的序列化逻辑(第一次输出完整对象,后续输出ID),同时写对应的反序列化器来处理ID到对象的映射。不过这种方式需要编写较多代码,除非特殊场景,不推荐使用。

内容的提问来源于stack exchange,提问作者Benedikt Gansinger

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 11:59:08