在 Jackson 的 JsonDeserializer 中获取检测到的泛型类型

2022-09-04 01:16:45

由于外部原因,我系统中的所有java s只能作为来自客户端的键值对列表接收,例如,a实际上将作为Json序列化接收。这意味着我需要自定义我的 Json 反序列化过程,以期望这种映射表示形式。MapMap<String, Book>List<MapEntry<String, Book>>

问题是这让我实现JsonDeserializer

deserialize(JsonParser p, DeserializationContext ctxt)

无法访问检测到的泛型类型的方法,它应该反序列化(在上面的示例中)。没有这些信息,我就无法在不失去类型安全性的情况下反序列化。Map<String, Book>List<MapEntry<String, Book>>

我正在研究转换器,但它给出的背景甚至更少。

例如:

public Map<K,V> convert(List<MapToListTypeAdapter.MapEntry<K,V>> list) {
    Map<K,V> x = new HashMap<>();
    list.forEach(entry -> x.put(entry.getKey(), entry.getValue()));
    return x;
}

但这可能会创建危险的地图,这些地图将引发检索,因为没有办法检查类型是否实际明智。有没有办法解决这个问题?ClassCastException

作为我所期望的一个例子,Gson的JsonDeserializer看起来像这样:

T deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)

即,它以一种理智的方式访问预期的类型。


答案 1

直接从作者那里得到了杰克逊谷歌小组的答案。

要了解的关键是 JsonDeserializers 创建/上下文化一次,并且它们仅在该时刻接收完整类型和其他信息。要获取此信息,反序列化程序需要实现 。调用其方法来初始化反序列化程序实例,并且有权访问也提供完整的 .ContextualDeserializercreateContextualBeanPropertyJavaType

所以它最终可能看起来像这样:

public class MapDeserializer extends JsonDeserializer implements ContextualDeserializer {

    private JavaType type;

    public MapDeserializer() {
    }

    public MapDeserializer(JavaType type) {
        this.type = type;
    }

    @Override
    public JsonDeserializer<?> createContextual(DeserializationContext deserializationContext, BeanProperty beanProperty) throws JsonMappingException {
        //beanProperty is null when the type to deserialize is the top-level type or a generic type, not a type of a bean property
        JavaType type = deserializationContext.getContextualType() != null 
            ? deserializationContext.getContextualType()
            : beanProperty.getMember().getType();            
        return new MapDeserializer(type);
    }

    @Override
    public Map deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
        //use this.type as needed
    }

    ...
}

注册并正常使用:

ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addDeserializer(Map.class, new MapDeserializer());
mapper.registerModule(module);

答案 2