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

如何在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设置:

  1. 创建MixIn类:
public abstract class AnnounceMixIn {
    @JsonProperty(value = "id", access = JsonProperty.Access.READ_WRITE)
    private Long id;
}
  1. 注册MixIn到ObjectMapper:
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.addMixIn(Announce.class, AnnounceMixIn.class);

关键注意点:

  • MixIn的字段名必须和原POJO的id完全一致
  • 仅修改access属性为READ_WRITE,value需和原注解保持一致
  • 无需复制其他属性的注解,ObjectMapper会自动合并原POJO的注解与MixIn的修改

方案二:自定义反序列化器(保留默认逻辑)

若MixIn仍有问题,可通过自定义反序列化器复用默认逻辑,仅手动处理id字段:

  1. 实现自定义反序列化器:
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;
    }
}
  1. 注册反序列化器:
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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 19:50:14