如何在父级对象中自定义Jackson对部分枚举属性的序列化
背景
我定义了一个泛型记录EnumJson,用来向前端传递带描述的枚举值:
public record EnumJson<E extends Enum<E>>(E value, String description) { }
还有一个接口的返回结构Enums,会单次返回多种不同枚举的列表(实际结构更复杂):
public record Enums( List<YesNo> yesNo, List<RedBlue> redBlue, List<UpDown> upDown, ...) { }
需求是:多数枚举用Jackson默认的Enum.name()序列化即可,但少数枚举(比如UpDown)需要自定义序列化逻辑,而且希望直接在Enums这个返回结构层面控制,比如像这样给泛型参数加注解实现:
public record Enums( List<YesNo> yesNo, List<RedBlue> redBlue, List<@SomeInstructionToCustomizeSerialization UpDown> upDown, ...) { }
目前我临时复制了EnumJson得到SpecialEnumJson,在其中给value字段加了序列化注解:
public record SpecialEnumJson<E extends Enum<E>>(@JsonSerialize(...) E value, String description) { }
但我觉得这个方案不够优雅,希望有更优的实现。
注意:给少数枚举添加自定义@JsonValue注解的方案不适用,因为会影响它们在整个应用中的序列化逻辑。
更优解决方案
方案1:直接在泛型参数上使用@JsonSerialize注解
不需要复制EnumJson,直接在Enums中需要自定义序列化的枚举列表的泛型参数上标注@JsonSerialize,指定对应的序列化器即可,仅影响当前字段的序列化行为,不会全局改变枚举的序列化逻辑。
步骤1:编写自定义序列化器
比如针对UpDown枚举写一个序列化器:
public class UpDownSerializer extends StdSerializer<UpDown> { protected UpDownSerializer() { super(UpDown.class); } @Override public void serialize(UpDown value, JsonGenerator gen, SerializerProvider provider) throws IOException { // 这里写你的自定义序列化逻辑,比如返回枚举的自定义编码、描述等 gen.writeString(value.getCustomCode()); } }
步骤2:在Enums中应用注解
直接在List的泛型参数上添加序列化注解:
public record Enums( List<YesNo> yesNo, List<RedBlue> redBlue, List<@JsonSerialize(using = UpDownSerializer.class) UpDown> upDown ) {}
这样Jackson在序列化这个upDown列表时,会使用你指定的序列化器处理每个UpDown枚举实例,而其他枚举还是用默认逻辑,完全满足需求,也不需要额外定义重复的SpecialEnumJson。
方案2:自定义注解+上下文序列化器(扩展性更强)
如果有多个枚举需要不同的自定义序列化,且希望注解更语义化,可以自定义一个专门用于泛型参数的注解,配合Jackson的ContextualSerializer实现更灵活的控制。
步骤1:自定义注解
@Target(ElementType.TYPE_USE) @Retention(RetentionPolicy.RUNTIME) public @interface CustomEnumSerialize { Class<? extends JsonSerializer<?>> serializer(); }
步骤2:实现上下文序列化器
写一个通用的枚举序列化器,能根据注解选择具体的实现:
public class ContextualEnumSerializer extends StdSerializer<Enum<?>> implements ContextualSerializer { private JsonSerializer<?> delegateSerializer; public ContextualEnumSerializer() { super(Enum.class); } private ContextualEnumSerializer(JsonSerializer<?> delegateSerializer) { super(Enum.class); this.delegateSerializer = delegateSerializer; } @Override public void serialize(Enum<?> value, JsonGenerator gen, SerializerProvider provider) throws IOException { if (delegateSerializer != null) { delegateSerializer.serialize(value, gen, provider); } else { // 默认用Enum.name()序列化 gen.writeString(value.name()); } } @Override public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) throws JsonMappingException { if (property != null) { // 获取泛型参数上的CustomEnumSerialize注解 AnnotatedType annotatedType = property.getMember().getAnnotatedType(); CustomEnumSerialize annotation = annotatedType.getAnnotation(CustomEnumSerialize.class); if (annotation != null) { try { JsonSerializer<?> serializer = annotation.serializer().getDeclaredConstructor().newInstance(); return new ContextualEnumSerializer(serializer); } catch (Exception e) { throw new JsonMappingException(property.getMember().getAnnotated(), "Failed to instantiate serializer", e); } } } return this; } }
步骤3:全局注册序列化器
在Jackson的ObjectMapper中注册这个上下文序列化器:
ObjectMapper mapper = new ObjectMapper(); SimpleModule module = new SimpleModule(); module.addSerializer(Enum.class, new ContextualEnumSerializer()); mapper.registerModule(module);
步骤4:在Enums中使用自定义注解
public record Enums( List<YesNo> yesNo, List<RedBlue> redBlue, List<@CustomEnumSerialize(serializer = UpDownSerializer.class) UpDown> upDown ) {}
这个方案适合有大量自定义序列化需求的场景,注解更语义化,扩展性更好,但配置稍复杂。
内容的提问来源于stack exchange,提问作者Werner de Groot

