如何在Jackson中为泛型创建自定义的反序列化器? [英] How to create a custom deserializer in Jackson for a generic type?
问题描述
class< T> FOO< T> {
....
}
class Bar {
Foo< Something> FOO;
}
我想为Foo编写一个定制的Jackson解串器。为了做到这一点(例如,为了反序列化 Bar
具有 Foo< Something>
属性的类),我需要知道 Bar
在反序列化时使用的 Foo
的具体类型(例如我需要要知道在特定情况下 T
是 Something
)。
如何写一个这样的解串器?这应该是可以做到的,因为杰克逊用类型化的集合和地图来做。
澄清:
看来问题的解决方案有两个部分:
$ b 1)获取声明的属性类型 foo
code> Bar ,并用它反序列化 Foo< Somehting>
2)在反序列化时间中发现我们正在反序列化类 Bar 中的属性
foo
in为了成功完成第1步)
完成1和2的过程如何?
您可以实现自定义 JsonDeserializer
为您的泛型类型,它也实现 ContextualDeserializer
。
例如,假设我们有下面是一个包含泛型值的简单包装类型:
public static class Wrapper< T> {
公共T值;
}
我们现在想要反序列化JSON,如下所示:
{
name:Alice,
age:37
}
转换为类似下面的类的实例:
public static class Person {
public Wrapper< String>名称;
public Wrapper< Integer>年龄;
$ / code>
实现 ContextualDeserializer
允许我们根据字段的泛型类型参数为 Person
类中的每个字段创建特定的反序列化器。这允许我们将该名称反序列化为字符串,并将年龄作为整数。
完整的反序列化器如下所示:
public static class WrapperDeserializer extends JsonDeserializer< Wrapper<>>实现ContextualDeserializer {
private JavaType valueType;
@Override
public JsonDeserializer<?> createContextual(DeserializationContext ctxt,BeanProperty属性)抛出JsonMappingException {
JavaType wrapperType = property.getType();
JavaType valueType = wrapperType.containedType(0);
WrapperDeserializer deserializer = new WrapperDeserializer();
deserializer.valueType = valueType;
返回反序列化器;
}
@Override
public Wrapper<?>反序列化(JsonParser解析器,DeserializationContext ctxt)抛出IOException {
Wrapper<?> wrapper = new Wrapper<>();
wrapper.value = ctxt.readValue(parser,valueType);
返回包装;
$ / code>
最好看看首先在这里创建文本
,因为这将被杰克逊首先调用。我们从 BeanProperty
(例如 Wrapper< String>
>)中读取字段的类型,然后提取第一个通用类型参数(例如 String
)。然后我们创建一个新的反序列化器并将内部类型存储为 valueType
。
一旦 为了注册这个自定义反序列化器,我们需要创建一个包含它的模块,并注册该模块: 如果我们尝试从上面反序列化示例JSON,我们可以看到它按预期工作: 关于上下文反串行器如何在杰克逊文档。 Imagine the following scenario: I want to write a custom Jackson deserializer for Foo. In order to do that (for example, in order to deserialize How does one write such a deserializer? It should be possible to do it, since Jackson does it with typed collections and maps. Clarifications: It seems there are 2 parts to solution of the problem: 1) Obtain declared type of property 2) Find out at deserialization time that we are deserializing property How does one complete 1 and 2 ? You can implement a custom For example, suppose we have the following simple wrapper type that contains a generic value: We now want to deserialize JSON that looks like this: into an instance of a class that looks like this: Implementing The complete deserializer looks like this: It is best to look at Once In order to register this custom deserializer, we then need to create a module that contains it, and register that module: If we then try to deserialize the example JSON from above, we can see that it works as expected: There are some more details about how contextual deserializers work in the Jackson documentation. 这篇关于如何在Jackson中为泛型创建自定义的反序列化器?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!在这个新创建的反序列化器上调用deserialize
,我们可以简单地要求Jackson将该值反序列化为内部类型而不是整个包装器类型,并返回一个新的 Wrapper
包含反序列化的值。
SimpleModule module = new SimpleModule()
.addDeserializer(Wrapper.class,new WrapperDeserializer());
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(module);
Person person = objectMapper.readValue(json,Person.class);
System.out.println(person.name.value); //打印Alice
System.out.println(person.age.value); // print 37
class <T> Foo<T> {
....
}
class Bar {
Foo<Something> foo;
}
Bar
class that has Foo<Something>
property), I need to know the concrete type of Foo<T>
, used in Bar
, at deserialization time (e.g. I need to know that T
is Something
in that particluar case).foo
inside Bar
and use that to deserialize Foo<Somehting>
foo
inside class Bar
in order to successfully complete step 1)JsonDeserializer
for your generic type which also implements ContextualDeserializer
.public static class Wrapper<T> {
public T value;
}
{
"name": "Alice",
"age": 37
}
public static class Person {
public Wrapper<String> name;
public Wrapper<Integer> age;
}
ContextualDeserializer
allows us to create a specific deserializer for each field in the Person
class, based on the generic type parameters of the field. This allows us to deserialize the name as a string, and the age as an integer.public static class WrapperDeserializer extends JsonDeserializer<Wrapper<?>> 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
here first, as this will be called first by Jackson. We read the type of the field out of the BeanProperty
(e.g. Wrapper<String>
) and then extract the first generic type parameter (e.g. String
). We then create a new deserializer and store the inner type as the valueType
.deserialize
is called on this newly created deserializer, we can simply ask Jackson to deserialize the value as the inner type rather than as the whole wrapper type, and return a new Wrapper
containing the deserialized value.SimpleModule module = new SimpleModule()
.addDeserializer(Wrapper.class, new WrapperDeserializer());
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(module);
Person person = objectMapper.readValue(json, Person.class);
System.out.println(person.name.value); // prints Alice
System.out.println(person.age.value); // prints 37