为何Jackson的JsonIdentityInfo仅返回数组中部分对象的ID?
问题原因与解决方案
核心原因
@JsonIdentityInfo的设计目的就是解决双向关联导致的循环序列化问题,它的工作逻辑是:
- Jackson在序列化过程中会维护一个对象引用缓存,当某个对象第一次出现时,会输出完整的JSON结构,并将该对象的ID存入缓存。
- 后续再遇到同一个对象的引用(通过内存实例判断),就直接输出该对象的ID,不再重复序列化完整结构。
你遇到的情况,本质是第二个对象对应的Attivita实例,在整个序列化流程中已经被处理过一次,因此Jackson自动用ID替代了完整结构。
解决方案
方案1:改用双向关联专用注解(推荐)
如果你的需求是序列化时避免循环引用,同时保证每个关联对象只在主动端输出完整结构,可以移除@JsonIdentityInfo,改用@JsonManagedReference和@JsonBackReference:
@JsonManagedReference标注在关联的主动端(一对多的一方),会序列化完整的关联列表@JsonBackReference标注在关联的被动端(多对一的一方),会被Jackson忽略,避免循环递归
修改后的实体代码:
@Data @Entity @Table(name = "attivita", schema = "public") @AllArgsConstructor @NoArgsConstructor @JsonIgnoreProperties({"hibernateLazyInitializer", "handler"}) public class Attivita implements Serializable { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id", nullable = false) private Long id; @JsonManagedReference @OneToMany(mappedBy = "attivita", cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true) private List<Lavorazione> listaLavorazioni; } @Data @Entity @Table(name = "lavorazione", schema = "public") @AllArgsConstructor @NoArgsConstructor @JsonIgnoreProperties({"hibernateLazyInitializer", "handler"}) public class Lavorazione implements Serializable { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id", nullable = false) private Long id; @JsonBackReference @ManyToOne(targetEntity = Attivita.class, fetch = FetchType.LAZY, optional = false) @JoinColumn(name="fk_attivita", nullable = false, updatable = false) private Attivita attivita; }
方案2:关闭Jackson对象身份识别(强制输出完整结构)
如果你确实需要所有对象都输出完整结构,且能确保不会出现循环引用,可以关闭Jackson的对象身份识别功能:
ObjectMapper mapper = new ObjectMapper(); // 关闭对象ID引用的自动替换 mapper.disable(MapperFeature.USE_OBJECT_ID_FOR_REFERENCES); String jsonResult = mapper.writeValueAsString(yourTargetList);
方案3:针对特定字段覆盖默认行为
如果需要保留@JsonIdentityInfo,但希望特定字段始终输出完整结构,可以在字段上添加@JsonIdentityReference(alwaysAsId = false):
// 在Lavorazione的attivita字段上添加 @JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id") @JsonIdentityReference(alwaysAsId = false) @ManyToOne(targetEntity = Attivita.class, fetch = FetchType.LAZY, optional = false) @JoinColumn(name="fk_attivita", nullable = false, updatable = false) private Attivita attivita;
注意:这种方式可能重新引发循环序列化问题,需要谨慎使用。
额外注意事项
- 确保JPA懒加载的关联对象在序列化前已经被初始化(比如使用
Hibernate.initialize()),否则Jackson可能会将未初始化的代理对象识别为“已处理过的对象”,从而输出ID而非完整结构。 - 如果你的返回列表中确实存在重复的
Attivita实例,Jackson的ID替换行为是符合预期的,此时若需要重复输出完整结构,必须采用方案2。
内容的提问来源于stack exchange,提问作者Danilo Giovannico
相关产品推荐
相关产品推荐

