【问题标题】:Jackson deserializer for generic type通用类型的杰克逊反序列化器
【发布时间】:2016-11-11 07:19:12
【问题描述】:

我需要为具有泛型的类编写自定义反序列化器。我找不到办法做到这一点,但我无法想象我是唯一一个遇到这个问题的人。据我考虑,有两种方法可以实现,但没有一种是可以实现的:

  1. 在反序列化器的构造函数中给反序列化器提供Class参数是行不通的,因为在注册反序列化器时,Type.class和反序列化器的实例与传递的Class实例的关系丢失了。

例如:

public class Foo<T> {}
public class FooDeserializer<T> {
    public FooDeserializer(Class<T> type) { ... }
    ...
}
// Boilerplate code...
module.addDeserializer(Foo.class, new FooDeserializer<Bar1>(Bar1.class));
module.addDeserializer(Foo.class, new FooDeserializer<Bar2>(Bar2.class));

这不起作用,当 ObjectMapper 实例获取 Foo 的实例时,没有可用的泛型参数的类型信息(类型擦除),因此它只是选择最后注册的反序列化器。

  1. 在类中保留泛型类型的引用无济于事,因为无法将类的实例化版本传递给反序列化器(接口为 readValue(String, Class))。

例如:

String json = "...";
ObjectMapper mapper = ...;
Foo<Bar1> foo = new Foo<>(Bar1.class);
foo = mapper.readValue(json, Foo.class); // Can't pass empty foo instance with Class<?> field containing Bar1.class

需要这样的东西:

mapper.readValue(json, Foo.class, Bar1.class); // Doesn't exist in jackson

有什么建议吗?

编辑: 我找到了解决问题的方法,但这不是一个干净的解决方案:

我使用 Class 字段扩展 FooDeserializer 以保存 Foo 的泛型参数的类型。然后,每次我想将一些 json 反序列化为一个新的 Foo 实例时,我都会得到一个新的 ObjectMapper 实例(我在工厂的预配置实例上使用 ObjectMapper#copy)并传递一个包含 FooDeserializer 实例的新模块类参数(我现在知道类型)。模块、FooDeserializer 和 ObjectMapper 副本是短暂的,它们仅在这个单一的反序列化操作中被实例化。正如我所说,不是很干净,但仍然比多次继承 Foo 并为每个子类编写一个反序列化器要好。

例子:

public class FooDeserializer<T> extends StdDeserializer<T> {
    private Class<T> type;
    public FooDeserializer(Class<T> type) { this.type = type }
    ...
}

// Meanwhile, before deserialization:
ObjectMapper mapper = MyObjectMapperFactory.get().copy();
SimpleModule module = new SimpleModule(new Version(0,0,1,null,null,null);
module.addDeserializer(Foo.class, new FooDeserializer(Bar1.class);
mapper.addModule(module);
Foo<Bar1> x = mapper.readValue(json, Foo.class); 

可能会将其放入实用方法中以隐藏丑陋。

【问题讨论】:

标签: java generics serialization jackson


【解决方案1】:

我认为您不需要编写我们自己的自定义反序列化器。您可以使用此语法反序列化使用泛型的对象,这些对象取自另一个 Stack Overflow 线程。

mapper.readValue(jsonString, new TypeReference<Data<String>>() {});

几个资源可以帮助您: Jackson - Deserialize using generic class http://www.tutorialspoint.com/jackson/jackson_data_binding_generics.htm

【讨论】:

  • 是的,这在某些情况下可能有效。但是,我的类型(示例中为 Foo)实现了 Collection 以及其他一些接口。因此杰克逊使用 CollectionDeserializer 而不是简单地映射到 Foo 类的字段。这不起作用,因为 CollectionDeserializer 期望 json 以“[”字符开头,但我的 json 以“{”字符开头,集合的 json 数组嵌套在根 json 对象中。
【解决方案2】:

您无需编写自定义反序列化程序。可以通过将 Jackson 注释放在作为参数传递给泛型类的类型上来反序列化泛型。
注释类Bar1如下:

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.WRAPPER_OBJECT)
@JsonTypeName("Bar1")
class Bar1 {

}

现在,当您反序列化 Foo&lt;Bar1&gt; 的实例时,Jackson 会将类型参数信息放入 JSON。然后可以将此 JSON 反序列化为泛型类 Foo.class,就像您反序列化任何其他类一样。

ObjectMapper mapper = ...;
Foo<Bar1> foo = new Foo<Bar1>();
String json = objectMapper.writeValueAsString(foo);
foo = mapper.readValue(json, Foo.class); // no need to specify the type Bar1.

所以如果每个可以作为参数传递给 Foo 的类都有注释,那么 JSON 可以在编译时不知道类型参数的情况下进行反序列化。

请参阅 Polymorphic DeserializationAnnotations 上的 Jackson 文档。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-23
    • 2014-01-31
    • 1970-01-01
    • 2021-12-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多