JSON反序列化异常:继承、List与子类场景下Object转LinkedHashMap
我定义了如下基类:
@Data @NoArgsConstructor @AllArgsConstructor @JsonTypeInfo( use = JsonTypeInfo.Id.NAME, property = "fieldType", include=JsonTypeInfo.As.EXISTING_PROPERTY, visible=true) @JsonSubTypes({ @JsonSubTypes.Type(value = JobCurrencyField.class, name = "CURRENCY") }) public abstract class BaseJobField { private String name; private Object value; private String fieldType; public static void main(String[] args) { ObjectMapper objectMapper = new ObjectMapper(); List<BaseJobField> jobFields = new ArrayList<>(); jobFields.add(JobCurrencyField.create("currencyUS", "USD", 10.4)); try { String fields = objectMapper.writeValueAsString(jobFields); List<BaseJobField> ret = objectMapper.readValue(fields, new TypeReference<List<BaseJobField>>(){}); JobCurrencyField jobCurrencyField = (JobCurrencyField) ret.get(0); JobCurrencyField.Currency currency = (JobCurrencyField.Currency)jobCurrencyField.getValue(); } catch (Exception x) { System.out.println(x.getMessage()); } } }
以及子类:
@Data @NoArgsConstructor @JsonTypeName("CURRENCY") public class JobCurrencyField extends BaseJobField { @Data @NoArgsConstructor public static class Currency { private String currencyName; private double amount; } public static JobCurrencyField create(String name, String currencyName, Double amount) { Currency currency = new Currency(); currency.setCurrencyName(currencyName); currency.setAmount(amount); JobCurrencyField jobCurrencyField = new JobCurrencyField(); jobCurrencyField.setName(name); jobCurrencyField.setValue(currency); jobCurrencyField.setFieldType("CURRENCY"); return jobCurrencyField; } }
执行objectMapper.readValue()反序列化时,内部类JobCurrencyField.Currency未被正确反序列化,而是被转为LinkedHashMap,导致JobCurrencyField.Currency currency = (JobCurrencyField.Currency)jobCurrencyField.getValue();抛出异常:java.util.LinkedHashMap cannot be cast to JobCurrencyField$Currency。
请问如何配置ObjectMapper,使其将value字段反序列化为Currency类型而非LinkedHashMap?
核心原因是BaseJobField中的value字段声明为Object类型,Jackson反序列化时无法推断其具体类型,只能默认转为LinkedHashMap。以下是几种可行的解决方法:
方法1:将基类改为泛型类
修改BaseJobField为泛型类,让子类指定value的具体类型,Jackson可通过泛型信息直接确定目标类型:
@Data @NoArgsConstructor @AllArgsConstructor @JsonTypeInfo( use = JsonTypeInfo.Id.NAME, property = "fieldType", include=JsonTypeInfo.As.EXISTING_PROPERTY, visible=true) @JsonSubTypes({ @JsonSubTypes.Type(value = JobCurrencyField.class, name = "CURRENCY") }) public abstract class BaseJobField<T> { private String name; private T value; private String fieldType; // main方法保持不变 }
子类修改为指定泛型参数:
@Data @NoArgsConstructor @JsonTypeName("CURRENCY") public class JobCurrencyField extends BaseJobField<JobCurrencyField.Currency> { // 内部类和create方法保持不变 }
这种方式逻辑清晰,是最优解。
方法2:在子类中重定义value字段并指定类型
若无法修改基类泛型,可在子类中重新声明value字段,指定具体类型,Jackson会优先使用子类的字段定义:
@Data @NoArgsConstructor @JsonTypeName("CURRENCY") public class JobCurrencyField extends BaseJobField { @Data @NoArgsConstructor public static class Currency { private String currencyName; private double amount; } // 重定义value字段,指定具体类型 private Currency value; public static JobCurrencyField create(String name, String currencyName, Double amount) { Currency currency = new Currency(); currency.setCurrencyName(currencyName); currency.setAmount(amount); JobCurrencyField jobCurrencyField = new JobCurrencyField(); jobCurrencyField.setName(name); jobCurrencyField.setValue(currency); jobCurrencyField.setFieldType("CURRENCY"); return jobCurrencyField; } }
注意:需确保@Data注解不会因字段重定义产生冲突,必要时手动实现getter/setter。
方法3:为value字段添加类型序列化注解
若不想修改类结构,可在value字段上添加@JsonTypeInfo注解,让Jackson在序列化时写入类型信息,反序列化时据此确定类型:
@Data @NoArgsConstructor @AllArgsConstructor @JsonTypeInfo( use = JsonTypeInfo.Id.NAME, property = "fieldType", include=JsonTypeInfo.As.EXISTING_PROPERTY, visible=true) @JsonSubTypes({ @JsonSubTypes.Type(value = JobCurrencyField.class, name = "CURRENCY") }) public abstract class BaseJobField { private String name; // 添加类型注解,写入类全限定名作为类型标识 @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "@class") private Object value; private String fieldType; // main方法保持不变 }
这种方式会在JSON的value对象中多出@class字段(如"@class": "com.example.JobCurrencyField$Currency"),Jackson反序列化时会根据该字段实例化正确的对象,但会增加JSON与Java类的耦合性,适合无法修改类结构的场景。
内容的提问来源于stack exchange,提问作者Fredo

