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

如何通过ObjectMapper向Jackson自定义序列化器传递上下文?

问题描述

自定义了一个用于字段脱敏的注解@JsonMask,绑定了自定义序列化器MaskingSerializer:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@JacksonAnnotationsInside
@JsonSerialize(using = MaskingSerializer.class)
public @interface JsonMask {
}

对应的序列化器实现:

public class MaskingSerializer extends JsonSerializer<String> {
    public static final String JSON_MASK_ENABLED_ATTRIBUTE = "JSON_MASK_ENABLED";

    @Override
    public void serialize(String value, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
        if (Boolean.TRUE.equals(serializerProvider.getConfig().getAttributes().getAttribute(JSON_MASK_ENABLED_ATTRIBUTE))) {
            jsonGenerator.writeString(RegExUtils.replaceAll(value, ".", "*"));
        } else {
            jsonGenerator.writeString(value);
        }
    }
}

尝试通过ObjectMapper传递上下文属性控制脱敏逻辑,但无论使用withAttribute、withPerCallAttribute还是withSharedAttribute,序列化器中获取到的ContextAttributes始终为空,无法生效:

SerializationConfig serializationConfig = objectMapper.getSerializationConfig();
serializationConfig.getAttributes().withAttribute(MaskingSerializer.JSON_MASK_ENABLED_ATTRIBUTE, Boolean.TRUE);
return objectMapper
        .setConfig(serializationConfig)
        .valueToTree(entity);
解决方案

1. 修复配置赋值问题(基础版)

Jackson的SerializationConfig是不可变对象,调用withAttribute等方法会返回新的配置实例,而非修改原对象。你需要将返回的新实例重新赋值,再设置给ObjectMapper:

// 获取原配置并创建新的带属性的配置实例
SerializationConfig newConfig = objectMapper.getSerializationConfig()
        .withAttribute(MaskingSerializer.JSON_MASK_ENABLED_ATTRIBUTE, Boolean.TRUE);
// 设置新配置并执行序列化
return objectMapper
        .setConfig(newConfig)
        .valueToTree(entity);

2. 推荐方案:使用ObjectWriter传递上下文(避免全局修改)

ObjectMapper是线程安全的,全局修改配置可能影响其他序列化场景。更安全的方式是使用ObjectWriter来传递单次序列化的上下文属性:

ObjectWriter writer = objectMapper.writer()
        .withAttribute(MaskingSerializer.JSON_MASK_ENABLED_ATTRIBUTE, Boolean.TRUE);
return writer.valueToTree(entity);

3. 进阶:实现ContextualSerializer接口(更灵活的上下文感知)

如果需要序列化器能根据字段或注解的额外信息动态调整,可以让MaskingSerializer实现ContextualSerializer接口,确保能正确获取上下文属性:

public class MaskingSerializer extends JsonSerializer<String> implements ContextualSerializer {
    public static final String JSON_MASK_ENABLED_ATTRIBUTE = "JSON_MASK_ENABLED";
    private Boolean maskEnabled;

    // 无参构造,用于初始创建
    public MaskingSerializer() {}

    // 带参数构造,用于上下文初始化
    public MaskingSerializer(Boolean maskEnabled) {
        this.maskEnabled = maskEnabled;
    }

    @Override
    public void serialize(String value, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
        boolean shouldMask = (maskEnabled != null) ? maskEnabled : 
                Boolean.TRUE.equals(serializerProvider.getConfig().getAttributes().getAttribute(JSON_MASK_ENABLED_ATTRIBUTE));
        if (shouldMask) {
            jsonGenerator.writeString(RegExUtils.replaceAll(value, ".", "*"));
        } else {
            jsonGenerator.writeString(value);
        }
    }

    @Override
    public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) throws JsonMappingException {
        // 从上下文获取属性,也可以结合注解的自定义参数
        Boolean enabled = Boolean.TRUE.equals(prov.getConfig().getAttributes().getAttribute(JSON_MASK_ENABLED_ATTRIBUTE));
        return new MaskingSerializer(enabled);
    }
}

这样修改后,序列化器能在初始化上下文时就获取到传递的属性,逻辑更清晰,也能支持更多自定义扩展。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 13:34:56