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

如何将JSON数组反序列化为对应Java枚举类型的Context POJO?

问题描述

给定如下JSON结构:

{
  // 其他字段
  "context": [
    {
      "id": "age",
      "name": "Age",
      "type": "string",
      "value": "730 days +"
    },
    {
      "id": "v2Score",
      "name": "V2 Score",
      "type": "number",
      "value": 5.9
    },
    {
      "id": "maturity",
      "name": "Maturity",
      "type": "string",
      "value": "High"
    },
    {
      "id": "coverage",
      "name": "Product Coverage",
      "type": "string",
      "value": "Low"
    },
    {
      "id": "threat_intensity",
      "name": "Threat Intensity",
      "type": "string",
      "value": "Very Low"
    },
    {
      "id": "threat_recency",
      "name": "Threat Recency",
      "type": "string",
      "value": "No recorded events"
    },
    {
      "id": "threat_sources",
      "name": "Threat Sources",
      "type": "string",
      "value": "No recorded events"
    }
  ]
}

需要将其中的context数组反序列化为如下Context POJO:

class Context {
  private final Age age;
  private final Maturity maturity;
  private final Coverage coverage;
  private final ThreatIntensity threatIntensity;
  private final ThreatRecency threatRecency;
  
  // 构造方法、Getter方法
}

其中所有字段均为枚举类型(以Age为例):

enum Age {
  @JsonProperty("0 - 7 days")
  LESS_THAN_ONE_WEEK,
  @JsonProperty("7 - 30 days")
  ONE_WEEK_TO_ONE_MONTH,
  @JsonProperty("30 - 365 days")
  ONE_MONTH_TO_ONE_YEAR, 
  @JsonProperty("365 - 730 days")
  ONE_TO_TWO_YEARS, 
  @JsonProperty("730 days +")
  MORE_THAN_TWO_YEARS
}

要求:

  • 忽略数组中id不在关注范围内的对象(如v2Score、threat_sources)
  • 利用id匹配枚举类型,value匹配具体枚举实例,忽略name、type字段
  • 若数组中缺失对应id的对象,对应枚举字段设为null
  • 避免硬编码switch语句,降低扩展时的维护成本

解决方案

方案1:基于@JsonCreator的轻量解析

通过将数组转为id-value映射,结合Jackson的ObjectMapper自动解析枚举,无需额外类定义。

步骤1:统一枚举标记(可选)

定义标记接口,方便后续扩展管理:

public interface ContextEnum {
}

让所有目标枚举实现该接口:

enum Age implements ContextEnum { /* ... 原有枚举值 ... */ }
enum Maturity implements ContextEnum { /* ... 原有枚举值 ... */ }
// Coverage、ThreatIntensity、ThreatRecency同理

步骤2:实现Context类的构造逻辑

在Context类中通过@JsonCreator接收数组,解析为目标枚举字段:

class Context {
    private final Age age;
    private final Maturity maturity;
    private final Coverage coverage;
    private final ThreatIntensity threatIntensity;
    private final ThreatRecency threatRecency;

    // 复用ObjectMapper实例,避免重复创建
    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();

    @JsonCreator
    public Context(@JsonProperty("context") List<Map<String, Object>> contextItems) {
        // 先将数组转为id->value的映射表
        Map<String, String> idValueMap = contextItems.stream()
                .filter(item -> item.containsKey("id") && item.containsKey("value"))
                .collect(Collectors.toMap(
                        item -> (String) item.get("id"),
                        item -> (String) item.get("value")
                ));

        // 逐个解析枚举,缺失则为null
        this.age = parseEnum(idValueMap.get("age"), Age.class);
        this.maturity = parseEnum(idValueMap.get("maturity"), Maturity.class);
        this.coverage = parseEnum(idValueMap.get("coverage"), Coverage.class);
        this.threatIntensity = parseEnum(idValueMap.get("threat_intensity"), ThreatIntensity.class);
        this.threatRecency = parseEnum(idValueMap.get("threat_recency"), ThreatRecency.class);
    }

    // 通用枚举解析方法
    private <T extends Enum<T>> T parseEnum(String value, Class<T> enumClass) {
        if (value == null) {
            return null;
        }
        try {
            // 利用Jackson自动处理@JsonProperty注解的映射
            return OBJECT_MAPPER.readValue("\"" + value + "\"", enumClass);
        } catch (IOException e) {
            // 解析失败时返回null,也可根据需求抛出异常
            return null;
        }
    }

    // Getter方法
    public Age getAge() { return age; }
    public Maturity getMaturity() { return maturity; }
    public Coverage getCoverage() { return coverage; }
    public ThreatIntensity getThreatIntensity() { return threatIntensity; }
    public ThreatRecency getThreatRecency() { return threatRecency; }
}

方案2:基于@JsonTypeInfo的注解驱动解析

通过为每个枚举定义对应的Item类,利用Jackson的类型注解自动匹配解析,完全遵循注解驱动风格。

步骤1:定义抽象ContextItem类及子类

// 基于id字段自动匹配子类
@JsonTypeInfo(
        use = JsonTypeInfo.Id.NAME,
        include = JsonTypeInfo.As.PROPERTY,
        property = "id",
        visible = true
)
@JsonSubTypes({
        @JsonSubTypes.Type(value = AgeItem.class, name = "age"),
        @JsonSubTypes.Type(value = MaturityItem.class, name = "maturity"),
        @JsonSubTypes.Type(value = CoverageItem.class, name = "coverage"),
        @JsonSubTypes.Type(value = ThreatIntensityItem.class, name = "threat_intensity"),
        @JsonSubTypes.Type(value = ThreatRecencyItem.class, name = "threat_recency")
})
abstract class ContextItem {
    private String id;
    // Getter方法
    public String getId() { return id; }
}

// 对应Age枚举的Item类
class AgeItem extends ContextItem {
    @JsonProperty("value")
    private Age age;
    public Age getAge() { return age; }
}

// 对应Maturity枚举的Item类
class MaturityItem extends ContextItem {
    @JsonProperty("value")
    private Maturity maturity;
    public Maturity getMaturity() { return maturity; }
}

// CoverageItem、ThreatIntensityItem、ThreatRecencyItem类同理

步骤2:实现Context类的构造逻辑

class Context {
    private final Age age;
    private final Maturity maturity;
    private final Coverage coverage;
    private final ThreatIntensity threatIntensity;
    private final ThreatRecency threatRecency;

    @JsonCreator
    public Context(@JsonProperty("context") List<ContextItem> contextItems) {
        // 初始化所有字段为null
        Age tempAge = null;
        Maturity tempMaturity = null;
        Coverage tempCoverage = null;
        ThreatIntensity tempIntensity = null;
        ThreatRecency tempRecency = null;

        // 遍历数组匹配对应的Item类
        for (ContextItem item : contextItems) {
            if (item instanceof AgeItem) {
                tempAge = ((AgeItem) item).getAge();
            } else if (item instanceof MaturityItem) {
                tempMaturity = ((MaturityItem) item).getMaturity();
            } else if (item instanceof CoverageItem) {
                tempCoverage = ((CoverageItem) item).getCoverage();
            } else if (item instanceof ThreatIntensityItem) {
                tempIntensity = ((ThreatIntensityItem) item).getThreatIntensity();
            } else if (item instanceof ThreatRecencyItem) {
                tempRecency = ((ThreatRecencyItem) item).getThreatRecency();
            }
        }

        this.age = tempAge;
        this.maturity = tempMaturity;
        this.coverage = tempCoverage;
        this.threatIntensity = tempIntensity;
        this.threatRecency = tempRecency;
    }

    // Getter方法
}

方案对比
方案优点缺点
方案1代码简洁,无需额外类定义,扩展时仅需添加一行解析代码需手动调用ObjectMapper,解析异常需自行处理
方案2完全遵循Jackson注解驱动风格,枚举解析自动完成需为每个枚举创建对应的Item类,代码量稍大

内容的提问来源于stack exchange,提问作者EarthTurtle

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 23:17:01