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

Jackson序列化Map<Investment, BigDecimal>出现对象引用问题咨询

Jackson序列化嵌套对象Map键异常问题解决方案

问题根因

你遇到的问题由两个底层机制导致:

  • Jackson默认序列化Map类型时,仅会调用键对象的toString()方法作为键的输出值,不会对键对象做结构化序列化,你看到的com.financial.detail.Investment@5d465e4b就是Investment类默认的toString()返回值。
  • Investment和MutualFund互相持有对方的引用,直接序列化会触发循环依赖,导致栈溢出异常。

问题1:是否可以仅通过Jackson注解解决?

可以,仅需少量注解即可满足需求,有两种常用实现方案:

方案1:输出结构化键值对数组(兼容JSON规范,优先推荐)

JSON规范要求对象的键必须为字符串类型,如果你可以接受将Map序列化为[ [投资对象1, 数值1], [投资对象2, 数值2] ]的数组格式,直接添加两个注解即可:

  1. 给两个实体类添加@JsonIdentityInfo注解处理循环依赖,避免栈溢出:
import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
import com.fasterxml.jackson.annotation.JsonFormat;

// 用shortName作为对象唯一标识,也可改用ObjectIdGenerators.UUIDGenerator生成唯一ID
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "shortName")
public class Investment implements Serializable{
    // 原有代码不变
}

// 同上添加注解
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "expenseRatio")
public class MutualFund implements Serializable{
    // 给underlyingInvestments添加数组格式序列化注解
    @JsonFormat(shape = JsonFormat.Shape.ARRAY)
    private Map<Investment, BigDecimal> underlyingInvestments;
    // 原有代码不变
}

方案2:将Investment序列化为JSON字符串作为Map键

如果你需要保留Map的对象结构,直接给underlyingInvestments字段指定键序列化器即可:

public class MutualFund implements Serializable{
    // 用Jackson自带的ToStringSerializer会自动调用对象的JSON序列化结果作为键
    @JsonSerialize(keyUsing = ToStringSerializer.class)
    private Map<Investment, BigDecimal> underlyingInvestments;
    // 原有代码不变
}

注意:该方案需要你重写Investment的toString()方法,返回你需要的JSON格式字符串,或者自定义简单的键序列化器实现。


问题2:自定义序列化器的最优实现思路

如果需要更灵活的序列化逻辑,自定义时遵循以下思路即可规避循环依赖问题:

  1. 不要手动维护对象引用缓存,复用Jackson内置的对象标识处理能力:自定义序列化器时通过SerializerProvider获取对应类型的序列化器,Jackson会自动识别已经序列化过的对象,避免循环输出。
  2. 键序列化器仅负责Investment的结构化输出:
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import java.io.IOException;

public class InvestmentKeySerializer extends StdSerializer<Investment> {
    public InvestmentKeySerializer() {
        super(Investment.class);
    }

    @Override
    public void serialize(Investment investment, JsonGenerator gen, SerializerProvider provider) throws IOException {
        // 直接复用Jackson的序列化逻辑,自动处理循环依赖
        provider.findValueSerializer(Investment.class).serialize(investment, gen, provider);
    }
}
  1. 给目标字段指定自定义序列化器即可:
@JsonSerialize(keyUsing = InvestmentKeySerializer.class)
private Map<Investment, BigDecimal> underlyingInvestments;

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.29 08:24:04