ObjectMapper请求响应转换后修改POJO值与结构问题如何解决
根本原因
Jackson默认对JSON对象的键仅支持字符串类型,当你使用自定义类VirtualCurrencyDto作为Map的键时:
- 序列化阶段:Jackson会自动调用
VirtualCurrencyDto的toString()方法生成JSON的键字符串,你使用Lombok的@Data注解生成的toString格式就是你看到的VirtualCurrencyDto(physicalCurrency=EUR, coinId=null),所以序列化输出的原始响应内容是正常的。 - 反序列化阶段: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
相关产品推荐
相关产品推荐

