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

如何在Jackson自定义JsonSerializer中序列化两个字段避免JSON字段重复

Jackson单个字段自定义序列化器同步生成两个字段避免重复输出

需求描述

使用Jackson进行JSON序列化时,希望通过给单个POJO字段标注的自定义JsonSerializer同时完成两个POJO字段的序列化,避免第二个字段被默认序列化逻辑处理,同时尽可能保持POJO代码简洁。

代码示例

POJO代码

public class Entity{

    // 两个字段是否加@JsonProperty都不影响行为
    
    @JsonSerialize(using=MySerializer.class)
    @HashTo("hashedKuku")
    private String kuku;

    private String hashedKuku;

    // 两个字段均有公共Getter、Setter

}

自定义序列化器MySerializer代码

public class MySerializer extends JsonSerializer<String> {

    // 为简化省略try-catch等非核心代码

    @Override
    public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
        gen.writeString(value);
        final String currentFieldName = gen.getOutputContext().getCurrentName();
        final Field declaredField = gen.getCurrentValue().getClass().getDeclaredField(currentFieldName);
        final HashTo annotation = declaredField.getAnnotation(HashTo.class);
        final String hashFieldName;
        if(annotation!=null && !(hashFieldName=annotation.value()).isEmpty()) {
            // gen.writeStringField(hashFieldName, new Hasher().getHashedValue(value));
            // 或者
            serializers.defaultSerializeField(hashFieldName, new Hasher().getHashedValue(value), gen);              
    }

}

预期与实际结果

预期序列化结果

{
    "kuku": "someValue",
    "hashedKuku": "someHash"
}

实际序列化结果(重复字段问题)

{
    "kuku": "someValue",
    "hashedKuku": "someHash",
    "hashedKuku": null
}

问题原因

MySerializer已经写入了hashedKuku字段,同时Jackson默认序列化器也会对hashedKuku字段执行序列化操作。

使用约束

  • 自定义MySerializer只能标注在字段上,不能使用类级序列化器;
  • POJO需尽量简洁,仅在kuku字段上添加2个注解即可。

不符合要求的可行方案

  • 给hashedKuku字段添加@JsonProperty(JsonProperty.Access.WRITE_ONLY)注解;
  • 给hashedKuku字段添加@JsonIgnore注解;
  • 给hashedKuku的getter方法添加@JsonIgnore注解;
    其余方案大多存在过度设计的问题,也不符合要求。

解决方案

通过自定义BeanSerializerModifier动态过滤被@HashTo指向的字段,无需修改POJO代码,完全符合约束要求:

步骤1:补充@HashTo注解定义

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface HashTo {
    String value();
}

步骤2:自定义BeanSerializerModifier实现字段过滤

public class HashToFieldFilterModifier extends BeanSerializerModifier {
    @Override
    public List<BeanPropertyWriter> changeProperties(SerializationConfig config, BeanDescription beanDesc, List<BeanPropertyWriter> beanProperties) {
        Set<String> ignoreFields = new HashSet<>();
        // 收集所有被@HashTo指向的目标字段
        for (Field field : beanDesc.getBeanClass().getDeclaredFields()) {
            HashTo hashTo = field.getAnnotation(HashTo.class);
            if (hashTo != null && !hashTo.value().isBlank()) {
                ignoreFields.add(hashTo.value());
            }
        }
        // 过滤掉需要忽略的字段,默认序列化器不会再处理这些字段
        return beanProperties.stream()
                .filter(property -> !ignoreFields.contains(property.getName()))
                .collect(Collectors.toList());
    }
}

步骤3:注册到ObjectMapper

ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new SimpleModule() {
    @Override
    public void setupModule(SetupContext context) {
        super.setupModule(context);
        context.addBeanSerializerModifier(new HashToFieldFilterModifier());
    }
});

方案优势

  • 完全符合使用约束:POJO仅需在kuku字段保留两个注解,无需修改hashedKuku字段的任何配置
  • 通用适配:所有标注了@HashTo的字段都会自动处理对应目标字段的过滤,无硬编码逻辑
  • 原有序列化器逻辑无需改动,可直接复用

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.08 12:09:02