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

Gson:如何仅在特定注解下使用自定义序列化器?

解决方案:基于自定义注解+BeanSerializerModifier实现精准ID序列化

听起来你遇到的场景很典型——既要给特定ID字段做自定义序列化(比如掩码),又不能用@JsonAdapter硬编码到类里,还得避免全局影响其他整数字段。结合Jackson的扩展能力,我推荐用自定义注解+BeanSerializerModifier的方案,完美适配你层级深的复杂模型,而且不需要修改现有类的序列化器注解。

1. 定义标记ID字段的自定义注解

先创建一个注解,用来标记所有需要做掩码处理的ID字段,这样不管层级多深,只要字段带这个注解,就能被识别:

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MaskedId {
    // 可按需添加参数,比如自定义掩码规则、保留位数等
}

2. 实现自定义ID序列化器

写一个专门处理ID掩码的序列化器,这里以“保留后四位,前缀用****代替”为例,你可以根据实际需求修改逻辑:

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;

import java.io.IOException;

public class MaskedIdSerializer extends StdSerializer<Integer> {

    public MaskedIdSerializer() {
        super(Integer.class);
    }

    @Override
    public void serialize(Integer id, JsonGenerator gen, SerializerProvider provider) throws IOException {
        if (id == null) {
            gen.writeNull();
            return;
        }
        // 自定义掩码逻辑,示例:保留后四位,前面替换为****
        String maskedValue = String.format("****%04d", id % 10000);
        gen.writeString(maskedValue);
        // 如果需要输出数字类型而非字符串,改用gen.writeNumber(...)即可
    }
}

3. 用BeanSerializerModifier拦截带注解的字段

这是核心步骤:通过Modifier遍历所有待序列化的字段,检查是否带有@MaskedId注解,若有则替换为我们的自定义序列化器。不管你的模型层级有多深,只要字段标记了注解,都会被自动处理:

import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.SerializationConfig;
import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;

import java.util.List;

public class MaskedIdSerializerModifier extends BeanSerializerModifier {

    private final MaskedIdSerializer maskedIdSerializer = new MaskedIdSerializer();

    @Override
    public List<BeanPropertyWriter> changeProperties(SerializationConfig config, BeanDescription beanDesc, List<BeanPropertyWriter> beanProperties) {
        for (BeanPropertyWriter writer : beanProperties) {
            // 检查当前字段是否带有@MaskedId注解
            if (writer.getAnnotation(MaskedId.class) != null) {
                // 替换该字段的序列化器
                writer.assignSerializer(maskedIdSerializer);
            }
        }
        return beanProperties;
    }
}

4. 注册到ObjectMapper生效

最后把这个Modifier注册到你的ObjectMapper实例中,这样所有通过该ObjectMapper序列化的对象,带@MaskedId注解的ID字段都会自动应用掩码逻辑:

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;

public class ObjectMapperConfig {

    public static ObjectMapper createMaskingObjectMapper() {
        ObjectMapper objectMapper = new ObjectMapper();
        SimpleModule module = new SimpleModule();
        // 注册自定义的SerializerModifier
        module.setSerializerModifier(new MaskedIdSerializerModifier());
        objectMapper.registerModule(module);
        return objectMapper;
    }
}

使用示例

在你的数据模型里,给需要掩码的ID字段加上@MaskedId注解即可,嵌套层级的字段也能生效:

public class User {
    @MaskedId
    private Integer userId;
    private String username;
    // 嵌套对象中的ID同样会被处理
    private Address address;
    // getter/setter 省略
}

public class Address {
    @MaskedId
    private Integer addressId;
    private String street;
    // getter/setter 省略
}

当你用注册了Modifier的ObjectMapper序列化User对象时,userId和address.addressId都会被掩码处理,其他整数字段则保持默认序列化逻辑。

额外扩展

  • 如果需要不同的掩码规则,可以给@MaskedId注解添加参数(比如maskPattern),然后在序列化器中根据参数调整逻辑。
  • 若需要反序列化时将掩码值还原为原ID,可采用类似思路,编写自定义反序列化器和BeanDeserializerModifier。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 08:57:32