使用Jackson反序列化泛型枚举时遇到的异常问题
Jackson泛型枚举反序列化问题解决方法
我有如下Java类结构:
public class Lame<E extends Enum<E>> { private Enum<E> abstractType; public Enum<E> getAbstractType() { return abstractType; } @JsonCreator public Lame(E type) { this.abstractType = type; } } public class InheritLame extends Lame<InheritLame.CounterType> { public enum CounterType { ONE, TWO, THREE; } public String name; @JsonCreator public InheritLame(CounterType type) { super(type); this.name = "NAME"; } public String getName() { return this.name; } }
当实例化InheritLame对象(传入CounterType.ONE)并序列化为JSON时,输出为:
{"abstractType":"ONE","name":"NAME"}
但将该JSON反序列化为InheritLame对象时,抛出异常:
com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `InheritLame$CounterType` from String "abstractType": not one of the values accepted for Enum class: [TWO, ONE, THREE] at [Source: (String)"{"abstractType":"ONE","name":"NAME"}"; line: 1, column: 2] at com.fasterxml.jackson.databind.exc.InvalidFormatException.from(InvalidFormatException.java:67)...
实际项目中存在近50种不同枚举类型对应基类的private Enum<T> abstractType字段,通过调整基类层级结构解决过于繁琐,尝试过多种Jackson注解未解决,寻求可行方案。
解决方案:给构造器参数添加@JsonProperty注解
问题根源在于Jackson无法自动识别构造器参数与JSON字段的映射关系。默认情况下,带@JsonCreator的单参数构造器会尝试将整个JSON对象作为参数传入,但构造器期望的是枚举类型,导致Jackson错误地将JSON字段名(而非字段值)当作枚举值解析。
只需在基类和子类的构造器参数上添加@JsonProperty注解,指定对应的JSON字段名即可解决:
修改后的基类:
public class Lame<E extends Enum<E>> { private Enum<E> abstractType; public Enum<E> getAbstractType() { return abstractType; } @JsonCreator public Lame(@JsonProperty("abstractType") E type) { this.abstractType = type; } }
修改后的子类:
public class InheritLame extends Lame<InheritLame.CounterType> { public enum CounterType { ONE, TWO, THREE; } public String name; @JsonCreator public InheritLame(@JsonProperty("abstractType") CounterType type) { super(type); this.name = "NAME"; } public String getName() { return this.name; } }
这样Jackson就能明确知晓构造器的type参数对应JSON中的abstractType字段,正确提取字段值"ONE"并反序列化为对应的枚举实例。
备选方案:使用自定义反序列化器(复杂场景)
如果上述方法无法满足需求(比如需要更复杂的类型推断),可以为基类的abstractType字段自定义反序列化器:
- 创建自定义反序列化器:
public class EnumDeserializer extends JsonDeserializer<Enum<?>> { @Override public Enum<?> deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { // 获取当前需要反序列化的目标类型 JavaType targetType = ctxt.getContextualType(); if (targetType == null || !targetType.isEnumType()) { throw new IllegalArgumentException("Target type is not an enum"); } Class<? extends Enum<?>> enumClass = (Class<? extends Enum<?>>) targetType.getRawClass(); String value = p.getValueAsString(); return Enum.valueOf(enumClass, value); } }
- 在基类字段上添加注解指定反序列化器:
public class Lame<E extends Enum<E>> { @JsonDeserialize(using = EnumDeserializer.class) private Enum<E> abstractType; // 其余代码不变 }
这种方法适用于需要动态推断枚举类型的场景,无需在每个构造器上添加注解,但实现相对复杂。
内容的提问来源于stack exchange,提问作者Robert Benkovitz
相关产品推荐
相关产品推荐

