如何通过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
相关产品推荐
相关产品推荐

