如何在Jackson中为generics类型创建自定义反序列化器?
想象一下以下场景:
class Foo { .... } class Bar { Foo foo; }
我想为Foo编写一个自定义Jackson解串器。 为了做到这一点(例如,为了反序列化具有Foo
属性的Bar
类),我需要知道在反序列化时在Bar
使用的Foo
的具体类型(例如,我需要知道)那个特定情况下T
就是Something
)。
如何编写这样的解串器? 应该可以这样做,因为jackson用类型集合和地图来做。
澄清:
似乎有2个部分来解决问题:
1)在Bar
获取声明的属性类型foo
并使用它来反序列化Foo
2)在反序列化时找出我们在类Bar
中对属性foo
进行反序列化以便成功完成步骤1)
如何完成1和2?
您可以为通用类型实现自定义JsonDeserializer
,它也实现了ContextualDeserializer
。
例如,假设我们有以下包含通用值的简单包装器类型:
public static class Wrapper { public T value; }
我们现在想要反序列化看起来像这样的JSON:
{ "name": "Alice", "age": 37 }
到一个看起来像这样的类的实例:
public static class Person { public Wrapper name; public Wrapper age; }
实现ContextualDeserializer
允许我们根据字段的generics类型参数为Person
类中的每个字段创建特定的反序列化器。 这允许我们将名称反序列化为字符串,将年龄反序列化为整数。
完整的反序列化器如下所示:
public static class WrapperDeserializer extends JsonDeserializer> implements ContextualDeserializer { private JavaType valueType; @Override public JsonDeserializer> createContextual(DeserializationContext ctxt, BeanProperty property) throws JsonMappingException { JavaType wrapperType = property.getType(); JavaType valueType = wrapperType.containedType(0); WrapperDeserializer deserializer = new WrapperDeserializer(); deserializer.valueType = valueType; return deserializer; } @Override public Wrapper> deserialize(JsonParser parser, DeserializationContext ctxt) throws IOException { Wrapper> wrapper = new Wrapper<>(); wrapper.value = ctxt.readValue(parser, valueType); return wrapper; } }
最好先在这里查看createContextual
,因为这将首先由Jackson调用。 我们从BeanProperty
读取字段的类型(例如Wrapper
),然后提取第一个generics类型参数(例如String
)。 然后,我们创建一个新的反序列化器并将内部类型存储为valueType
。
一旦在这个新创建的反deserialize
上调用反序列化,我们就可以简单地要求Jackson将值反序列化为内部类型而不是整个包装器类型,并返回包含反序列化值的新Wrapper
。
为了注册这个自定义反序列化器,我们需要创建一个包含它的模块,并注册该模块:
SimpleModule module = new SimpleModule() .addDeserializer(Wrapper.class, new WrapperDeserializer()); ObjectMapper objectMapper = new ObjectMapper(); objectMapper.registerModule(module);
如果我们尝试从上面反序列化示例JSON,我们可以看到它按预期工作:
Person person = objectMapper.readValue(json, Person.class); System.out.println(person.name.value); // prints Alice System.out.println(person.age.value); // prints 37
关于上下文反序列化器如何在Jackson文档中工作的更多细节。
如果目标本身是generics类型,那么属性将为null,因为您需要从DeserializationContext获取valueTtype:
@Override public JsonDeserializer> createContextual(DeserializationContext ctxt, BeanProperty property) throws JsonMappingException { if (property == null) { // context is generic JMapToListParser parser = new JMapToListParser(); parser.valueType = ctxt.getContextualType().containedType(0); return parser; } else { // property is generic JavaType wrapperType = property.getType(); JavaType valueType = wrapperType.containedType(0); JMapToListParser parser = new JMapToListParser(); parser.valueType = valueType; return parser; } }