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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 17:10:18