Jackson反序列化含泛型Projection的JSON响应至Record的问题
解决方案
核心问题是JSON序列化框架(如Jackson)无法直接实例化抽象的Projection接口,需要明确如何动态匹配对应的具体实现类。以下是几种无需为每个实现编写单独解析逻辑的方案:
1. Jackson多态类型映射(推荐,侵入性低)
通过注解配置类型标识,让Jackson自动根据JSON中的标识字段选择实现类,后续新增实现仅需补充注解配置。
配置Projection接口
import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonTypeInfo; @JsonTypeInfo( use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type" // 用于区分实现类的JSON字段名 ) @JsonSubTypes({ @JsonSubTypes.Type(value = FooProjection.class, name = "foo"), @JsonSubTypes.Type(value = BarProjection.class, name = "bar") // 新增实现时在此添加@Type注解即可 }) public interface Projection { // 定义通用接口方法 }
定义Response Record
public record Response(String name, Projection projection) {}
调整JSON响应格式
让服务返回的projection对象包含type字段,示例JSON:
{ "name": "demo", "projection": { "type": "foo", "fooSpecificField": "example" } }
Jackson会自动根据type字段的值匹配对应的实现类完成反序列化。若无法修改服务返回格式,可改用@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS),但会耦合类全路径,不推荐。
2. 自定义全局反序列化器(适配无类型标识的场景)
如果服务返回的JSON没有类型标识字段,可通过字段特征推断实现类,自定义反序列化器统一处理。
编写反序列化器
import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.JsonNode; import java.io.IOException; public class ProjectionDeserializer extends JsonDeserializer<Projection> { @Override public Projection deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { JsonNode node = p.getCodec().readTree(p); // 根据字段特征判断具体实现类 if (node.has("fooSpecificField")) { return p.getCodec().treeToValue(node, FooProjection.class); } else if (node.has("barSpecificField")) { return p.getCodec().treeToValue(node, BarProjection.class); } throw new IllegalArgumentException("未知的Projection类型"); } }
注册反序列化器到接口
import com.fasterxml.jackson.databind.annotation.JsonDeserialize; @JsonDeserialize(using = ProjectionDeserializer.class) public interface Projection {}
Response Record保持不变
public record Response(String name, Projection projection) {}
后续新增实现时,仅需在ProjectionDeserializer中补充字段判断逻辑即可。
3. Record静态工厂方法(手动控制解析逻辑)
通过Response的静态工厂方法封装解析逻辑,无需依赖框架注解,完全自主控制。
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.JsonNode; import java.io.IOException; public record Response(String name, Projection projection) { public static Response fromJson(String json) throws IOException { ObjectMapper mapper = new ObjectMapper(); JsonNode root = mapper.readTree(json); String name = root.get("name").asText(); JsonNode projNode = root.get("projection"); Projection projection; if (projNode.has("fooSpecificField")) { projection = mapper.treeToValue(projNode, FooProjection.class); } else if (projNode.has("barSpecificField")) { projection = mapper.treeToValue(projNode, BarProjection.class); } else { throw new IllegalArgumentException("未知的Projection类型"); } return new Response(name, projection); } }
使用时直接调用Response.fromJson(jsonString)即可完成反序列化,新增实现仅需修改工厂方法内的判断分支。
内容的提问来源于stack exchange,提问作者haeger
相关产品推荐
相关产品推荐

