在 Jackson 的 JsonDeserializer 中获取检测到的泛型类型
Get the detected generic type inside Jackson's JsonDeserializer
由于外部原因,我系统中的所有 java Map
只能作为 列表 键值对从客户端接收,例如Map<String, Book>
实际上将被接收为 Json-序列化 List<MapEntry<String, Book>>
。这意味着我需要自定义我的 Json 反序列化过程以期望这种地图表示形式。
问题是 JsonDeserializer
让我实施
deserialize(JsonParser p, DeserializationContext ctxt)
方法无法访问它应该反序列化的检测到的泛型类型(上例中的 Map<String, Book>
)。没有这些信息,我就不能反序列化 List<MapEntry<String, Book>>
而不会失去类型安全性。
我正在查看 Converter,但它提供的上下文更少。
例如
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)
即它以一种理智的方式提供对预期类型的访问。
直接从作者那里得到了 Jackson Google group 的答案。
要理解的关键是 JsonDeserializer
是 created/contextualized 一次 ,并且它们仅在那一刻收到完整的类型和其他信息。要获取此信息,反序列化器需要实现 ContextualDeserializer
。它的 createContextual
方法被调用来初始化反序列化器实例,并且可以访问 BeanProperty
,它还提供了完整的 JavaType
.
所以它最终可能看起来像这样:
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);
由于外部原因,我系统中的所有 java Map
只能作为 列表 键值对从客户端接收,例如Map<String, Book>
实际上将被接收为 Json-序列化 List<MapEntry<String, Book>>
。这意味着我需要自定义我的 Json 反序列化过程以期望这种地图表示形式。
问题是 JsonDeserializer
让我实施
deserialize(JsonParser p, DeserializationContext ctxt)
方法无法访问它应该反序列化的检测到的泛型类型(上例中的 Map<String, Book>
)。没有这些信息,我就不能反序列化 List<MapEntry<String, Book>>
而不会失去类型安全性。
我正在查看 Converter,但它提供的上下文更少。
例如
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)
即它以一种理智的方式提供对预期类型的访问。
直接从作者那里得到了 Jackson Google group 的答案。
要理解的关键是 JsonDeserializer
是 created/contextualized 一次 ,并且它们仅在那一刻收到完整的类型和其他信息。要获取此信息,反序列化器需要实现 ContextualDeserializer
。它的 createContextual
方法被调用来初始化反序列化器实例,并且可以访问 BeanProperty
,它还提供了完整的 JavaType
.
所以它最终可能看起来像这样:
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);