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

ObjectMapper请求响应转换后修改POJO值与结构问题如何解决

根本原因

Jackson默认对JSON对象的键仅支持字符串类型,当你使用自定义类VirtualCurrencyDto作为Map的键时:

  1. 序列化阶段:Jackson会自动调用VirtualCurrencyDto的toString()方法生成JSON的键字符串,你使用Lombok的@Data注解生成的toString格式就是你看到的VirtualCurrencyDto(physicalCurrency=EUR, coinId=null),所以序列化输出的原始响应内容是正常的。
  2. 反序列化阶段:Jackson不知道如何把JSON的字符串键还原为VirtualCurrencyDto实例,默认会匹配该类参数数量最少的构造函数进行实例化。刚好你的VirtualCurrencyDto存在单String参数的构造函数public VirtualCurrencyDto(String coinId),因此Jackson会把整个键字符串直接赋值给coinId字段,导致physicalCurrency为null,和你遇到的异常结构完全吻合。
    PUT请求的参数反序列化遵循完全相同的逻辑,因此也会出现相同的错乱问题。
解决方案

给你提供两种可行的修复方案:

  • 方案1:自定义Map键的序列化/反序列化逻辑(推荐,无需修改现有业务结构)
    首先自定义键的序列化器和反序列化器:
// 键序列化器,保持和原toString一致的输出即可,也可以自定义更简洁的格式
public class VirtualCurrencyDtoKeySerializer extends StdSerializer<VirtualCurrencyDto> {
    public VirtualCurrencyDtoKeySerializer() {
        super(VirtualCurrencyDto.class);
    }

    @Override
    public void serialize(VirtualCurrencyDto value, JsonGenerator gen, SerializerProvider provider) throws IOException {
        // 直接用原toString逻辑,也可以自己定义成"EUR"、"USD"或者coinId的原值
        gen.writeFieldName(value.toString());
    }
}

// 键反序列化器,解析字符串键为VirtualCurrencyDto实例
public class VirtualCurrencyDtoKeyDeserializer extends KeyDeserializer {
    private static final Pattern PHYSICAL_CURRENCY_PATTERN = Pattern.compile("physicalCurrency=(\\w+),");
    private static final Pattern COIN_ID_PATTERN = Pattern.compile("coinId=(\\w+),");

    @Override
    public Object deserializeKey(String key, DeserializationContext ctxt) throws IOException {
        // 解析physicalCurrency
        Matcher currencyMatcher = PHYSICAL_CURRENCY_PATTERN.matcher(key);
        if (currencyMatcher.find() && !"null".equals(currencyMatcher.group(1))) {
            return new VirtualCurrencyDto(DTOCurrency.valueOf(currencyMatcher.group(1)));
        }
        // 解析coinId
        Matcher coinMatcher = COIN_ID_PATTERN.matcher(key);
        if (coinMatcher.find() && !"null".equals(coinMatcher.group(1))) {
            return new VirtualCurrencyDto(coinMatcher.group(1));
        }
        throw new IllegalArgumentException("无效的VirtualCurrencyDto键: " + key);
    }
}

然后在你的FooDto的pricing字段上添加注解指定序列化反序列化器即可:

@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class FooDto implements Serializable {
  private String name;
  @JsonSerialize(keyUsing = VirtualCurrencyDtoKeySerializer.class)
  @JsonDeserialize(keyUsing = VirtualCurrencyDtoKeyDeserializer.class)
  private Map<VirtualCurrencyDto, PriceDto> pricing = new LinkedHashMap<>();
}
  • 方案2:调整Map键类型为String(改动最小)
    直接把pricing字段的类型改为Map<String, PriceDto>,业务层自己处理字符串键到VirtualCurrencyDto的转换,不需要额外自定义序列化逻辑,适合快速修复问题。

内容的提问来源于stack exchange,提问作者mr nooby noob

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.28 06:36:04