使用@JsonTypeInfo反序列化含额外字段的混合类型JSON数组报错
解决Jackson混合类型数组解析时同级字段映射异常问题
问题根源
你当前的Java模型使用@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT),Jackson会认为每个OperationElement实例必须完全被extension或core节点包裹(即整个对象是{"extension": {...}}或{"core": {...}})。但你的JSON结构中,name、tags是和extension/core同级的顶层字段,超出了Jackson预期的包裹范围,导致无法映射这些字段,抛出MismatchedInputException。
最优解决方案:调整模型结构匹配JSON层级
直接修改Java模型,让它完全对应JSON的层级结构:创建一个Operation类来承载同级的name、tags字段,再用一个抽象类OperationContent来处理extension/core的混合类型。
修改后的完整代码:
@Data public class Payload { private List<Operation> operations; @Data public static class Operation { // 直接映射JSON中的同级字段 private String name; private String tags; // 用WRAPPER_OBJECT识别混合类型的content字段 @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.WRAPPER_OBJECT) @JsonSubTypes({ @JsonSubTypes.Type(value = Extension.class, name = "extension"), @JsonSubTypes.Type(value = Core.class, name = "core") }) private OperationContent content; } // 抽象父类定义混合类型的公共标识 @Data public static abstract class OperationContent {} // Extension类型对应JSON中的extension节点 @Data public static class Extension extends OperationContent { private String serviceType; private String serviceId; } // Core类型对应JSON中的core节点 @Data public static class Core extends OperationContent { private Config config; private String displayName; @Data public static class Config { private Long a; private Long b; } } public static void main(String[] args) throws Exception { ObjectMapper mapper = new ObjectMapper(); Payload operationsPayload = mapper.readValue( Files.readAllBytes(Paths.get("C:\\desc", "file.json")), Payload.class); System.out.println(operationsPayload); } }
方案说明
- 层级对应:
Operation类完全匹配JSON中每个operations元素的结构,name、tags直接映射同级字段,content字段对应extension/core节点。 - 类型识别:通过
@JsonTypeInfo(include = WRAPPER_OBJECT),Jackson会自动识别extension或core节点,并将其解析为对应的Extension或Core实例,赋值给content字段。 - 无额外配置:不需要自定义解析器或复杂注解,完全利用Jackson原生注解实现,代码简洁易维护。
备选方案:自定义反序列化器(保留原模型结构)
如果无法修改模型结构,可以通过自定义反序列化器手动处理字段映射:
步骤1:自定义反序列化器
public static class OperationElementDeserializer extends StdDeserializer<OperationElement> { protected OperationElementDeserializer() { super(OperationElement.class); } @Override public OperationElement deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { JsonNode node = p.readValueAsTree(); ObjectMapper mapper = (ObjectMapper) p.getCodec(); OperationElement element; // 根据存在的节点判断类型 if (node.has("extension")) { element = mapper.treeToValue(node, Extension.class); } else if (node.has("core")) { element = mapper.treeToValue(node, Core.class); } else { throw new IllegalArgumentException("未知的操作类型"); } // 手动设置父类的name和tags字段 element.setName(node.get("name").asText()); element.setTags(node.get("tags").asText()); return element; } }
步骤2:修改抽象类注解
在OperationElement上添加自定义反序列化器注解:
@Data @JsonDeserialize(using = OperationElementDeserializer.class) public static abstract class OperationElement { private String name; private String tags; }
步骤3:移除原类型注解
删除OperationElement上的@JsonTypeInfo和@JsonSubTypes注解,以及子类的@JsonRootName注解,让自定义反序列化器接管类型判断和字段映射。
内容的提问来源于stack exchange,提问作者SoT
相关产品推荐
相关产品推荐

