如何在ObjectMapper中覆盖Announce类id字段的@JsonProperty只读限制?
解决Announce类id字段反序列化问题的可行方案
问题背景
有一个无法修改源码的模型类Announce,其id字段定义如下:
public class Announce { @JsonProperty(value = "id", access = JsonProperty.Access.READ_ONLY) @Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "announce_sequence") @SequenceGenerator(name = "announce_sequence", sequenceName = "announce_id_sequence", allocationSize = 100) @Setter(AccessLevel.NONE) private Long id; // 其他46个复杂属性 }
由于id字段标注了access = JsonProperty.Access.READ_ONLY,ObjectMapper无法反序列化JSON中的id值。当前限制条件:
- 无法修改原POJO源码
- 不能禁用
MapperFeature.USE_ANNOTATIONS,其他46个属性的注解必须保留 - 不想编写全新的DTO
- 尝试过mixIn但未成功
- 考虑自定义反序列化器,但希望保留默认反序列化逻辑仅补全id字段
方案一:正确使用MixIn覆盖注解
MixIn方案完全可行,之前未成功大概率是写法有误。正确的MixIn只需针对id字段重定义注解,覆盖原有的READ_ONLY设置:
- 创建MixIn类:
public abstract class AnnounceMixIn { @JsonProperty(value = "id", access = JsonProperty.Access.READ_WRITE) private Long id; }
- 注册MixIn到ObjectMapper:
ObjectMapper objectMapper = new ObjectMapper(); objectMapper.addMixIn(Announce.class, AnnounceMixIn.class);
关键注意点:
- MixIn的字段名必须和原POJO的
id完全一致 - 仅修改
access属性为READ_WRITE,value需和原注解保持一致 - 无需复制其他属性的注解,ObjectMapper会自动合并原POJO的注解与MixIn的修改
方案二:自定义反序列化器(保留默认逻辑)
若MixIn仍有问题,可通过自定义反序列化器复用默认逻辑,仅手动处理id字段:
- 实现自定义反序列化器:
public class AnnounceDeserializer extends StdDeserializer<Announce> { private final JsonDeserializer<Object> defaultDeserializer; public AnnounceDeserializer(JsonDeserializer<Object> defaultDeserializer) { super(Announce.class); this.defaultDeserializer = defaultDeserializer; } @Override public Announce deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { // 先用默认反序列化器处理所有属性 Announce announce = (Announce) defaultDeserializer.deserialize(p, ctxt); // 手动读取并设置id字段 JsonNode node = p.getCodec().readTree(p); if (node.has("id")) { Long id = node.get("id").asLong(); // 通过反射突破private权限和无setter限制 try { Field idField = Announce.class.getDeclaredField("id"); idField.setAccessible(true); idField.set(announce, id); } catch (NoSuchFieldException | IllegalAccessException e) { throw new IOException("Failed to set id field", e); } } return announce; } }
- 注册反序列化器:
ObjectMapper objectMapper = new ObjectMapper(); SimpleModule module = new SimpleModule(); // 获取默认反序列化器,避免重复实现全量属性逻辑 JsonDeserializer<Object> defaultDeserializer = objectMapper.getDeserializationConfig() .findRootValueDeserializer(objectMapper.constructType(Announce.class)); module.addDeserializer(Announce.class, new AnnounceDeserializer(defaultDeserializer)); objectMapper.registerModule(module);
方案三:使用BeanDeserializerModifier修改字段配置
通过BeanDeserializerModifier直接修改默认反序列化器中id字段的访问配置,无需额外类:
ObjectMapper objectMapper = new ObjectMapper(); objectMapper.registerModule(new SimpleModule() { @Override public void setupModule(SetupContext context) { context.addBeanDeserializerModifier(new BeanDeserializerModifier() { @Override public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config, BeanDescription beanDesc, JsonDeserializer<?> deserializer) { if (beanDesc.getBeanClass() == Announce.class) { BeanDeserializer baseDeserializer = (BeanDeserializer) deserializer; // 遍历属性找到id字段并修改访问权限 Iterator<SettableBeanProperty> properties = baseDeserializer.properties(); while (properties.hasNext()) { SettableBeanProperty property = properties.next(); if ("id".equals(property.getName())) { property.setAccess(JsonProperty.Access.READ_WRITE); } } } return deserializer; } }); } });
内容的提问来源于stack exchange,提问作者Rafael Lima
相关产品推荐
相关产品推荐

