如何将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
相关产品推荐
相关产品推荐

