Jackson基于属性值从类型注册表获取类型反序列化泛型类型问题
实现Jackson泛型反序列化时自定义反序列化器递归调用问题
需求说明
我需要实现JSON到泛型类型的反序列化逻辑:以JSON中的属性值作为泛型参数T的类型参考,从自定义类型注册表中获取对应目标类型完成反序列化。
目前已完成概念验证:只要通过TypeFactory.constructParametricType传入正确的JavaType,即可成功反序列化泛型类型。但在自定义实现JsonDeserializer时,由于该反序列化器注册在Property.class上,会出现递归调用自身的问题。
我曾尝试通过新建ObjectMapper实例规避该问题,该方案可正常运行,但我不希望额外创建新的ObjectMapper实例,期望复用同一个实例完成反序列化。
注:我知晓Jackson提供的相关注解方案,但由于类型注册表需要适配特定业务逻辑,无法使用注解实现。
当前可运行的测试代码
import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.databind.type.TypeFactory; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; class Scratch { interface Type { @JsonIgnore public default String getName() { return this.getClass().getSimpleName(); } } static class Property<T extends Type> { private String typeName; private T type; private String name; public Property() { } public Property(T type, String name) { this.typeName = type.getName(); this.type = type; this.name = name; } public String getTypeName() { return typeName; } public void setTypeName(String typeName) { this.typeName = typeName; } public T getType() { return type; } public void setType(T type) { this.type = type; this.typeName = type.getName(); } public String getName() { return name; } public void setName(String name) { this.name = name; } } static class TypeRegistry { private final Map<String, Class<? extends Type>> types = new HashMap<>(); public void add(Type type) { types.put(type.getName(), type.getClass()); } public boolean has(String typeName) { return types.containsKey(typeName); } public Class<? extends Type> get(String typeName) { return this.types.get(typeName); } } static class TextType implements Type { private String color; public String getColor() { return color; } public void setColor(String color) { this.color = color; } } static class NumberType implements Type { private String format; public String getFormat() { return format; } public void setFormat(String format) { this.format = format; } } public static void main(String[] args) throws Exception { var typeRegistry = new TypeRegistry(); typeRegistry.add(new TextType()); typeRegistry.add(new NumberType()); var parameters = new ArrayList<Property<?>>(); parameters.add(new Property<>(new TextType(), "text_field")); parameters.add(new Property<>(new NumberType(), "number_field")); var module = new SimpleModule(); module.addDeserializer(Property.class, new JsonDeserializer<Property<?>>() { private final ObjectMapper newObjectMapper = new ObjectMapper(); @Override public Property<?> deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException { JsonNode node = p.getCodec().readTree(p); if (node == null || node.isNull()) { return null; } var typeName = node.get("typeName").asText(); var type = typeRegistry.get(typeName); return newObjectMapper.readValue( node.toString(), ctxt.getTypeFactory().constructParametricType(Property.class, type) ); } }); var objectMapper = new ObjectMapper(); var desObjectMapper = new ObjectMapper(); desObjectMapper.registerModule(module); var jsonSingle = objectMapper.writeValueAsString(parameters.get(0)); var json = objectMapper.writeValueAsString(parameters); var parsedSingle = objectMapper.readValue( jsonSingle, TypeFactory.defaultInstance().constructParametricType( Property.class, typeRegistry.get("TextType") ) ); var parsedProperties = desObjectMapper.readValue( json, new TypeReference<List<Property<?>>>() {} ); } }
补充参考方案
以下是无需新建ObjectMapper实例的实现思路:先将JSON反序列化为通用Map结构,取出类型标识后再通过convertValue转换为目标泛型类型:
var parsedProperties = objectMapper.readValue( json, new TypeReference<List<LinkedHashMap<String, Object>>>() { } ); var castedProperties = new ArrayList<Property<?>>(); for (var parsedProperty : parsedProperties) { var typeName = (String) parsedProperty.get("typeName"); castedProperties.add( objectMapper.convertValue( parsedProperty, TypeFactory.defaultInstance().constructParametricType( Property.class, typeRegistry.get(typeName) ) ) ); }
内容的提问来源于stack exchange,提问作者Aurimas Niekis
相关产品推荐
相关产品推荐

