【问题标题】:Why Jackson’s ObjectMapper methods take raw TypeReference为什么 Jackson 的 ObjectMapper 方法采用原始的 TypeReference
【发布时间】:2018-07-02 06:16:00
【问题描述】:

Jackson 2 ObjectMapper 类定义了许多通用方法,用于将 JSON 字符串、字节数组、文件等反序列化为给定的 Java 类型。

目标类型在这些方法的参数中给出。

例如。类型可以以Class<T> 的形式给出,就像在方法中一样

public <T> T readValue(String content, Class<T> valueType)

它返回相同的T 对象(因此是类型安全的)。

但它也可以以TypeReference&lt;T&gt; 的形式给出(可以编码复杂的泛型类型),因此可以构建例如。 new TypeReference&lt;List&lt;Long&gt;&gt; { } 告诉 ObjectMapper 输入需要反序列化为 long 列表——它可以传递,例如。进入方法:

@SuppressWarnings({ "unchecked", "rawtypes" })
public <T> T readValue(String content, TypeReference valueTypeRef)

但该方法采用 raw TypeReference 而不是泛型 TypeReference&lt;T&gt;,因此需要手动向调用添加泛型参数:

objectMapper.<List<Long>>readValue(input, listOfLongs)

如果在提供的类型中出现错误,编译器将无法捕捉到。如果方法签名是,那将不是问题

public <T> T readValue(String content, TypeReference<T> valueTypeRef)

这将告诉编译器返回的值始终与提供的TypeReference 的泛型参数的类型相同,类似于它与Class&lt;T&gt; 的工作方式。

我的问题是——这种 API 背后的原因是什么? 为什么 Jackson 方法采用原始的 TypeReference?当返回的对象确实与TypeReference的泛型参数引用的类型不同时,是否存在任何有效情况?

PS:同样令我困惑的是,对应的convertValue 方法采用的不是原始类型,而是通配符:

public <T> T convertValue(Object fromValue, TypeReference<?> toValueTypeRef)

同样readValues:

public <T> MappingIterator<T> readValues(JsonParser p, TypeReference<?> valueTypeRef)

readValue(JsonParser, TypeReference)实际上是takes a fully qualified generic parameter

public <T> T readValue(JsonParser p, TypeReference<T> valueTypeRef)

【问题讨论】:

    标签: java generics jackson jackson2


    【解决方案1】:

    它已经是reported as an issue,但被标记为3.x

    该库的作者有一条评论解释了为什么尚未更正此问题:

    看起来像ObjectMapper API(也许还有ObjectReader)省略了与TypeReference 匹配的类型变量。这将是有意义的更改,但可能会导致一些源兼容性问题(不是二进制),所以也许在 3.0 中而不是更早的版本中这样做。

    【讨论】:

    • 似乎它已在 2.10 中得到修复(尽管截至发表此评论时,问题尚未解决)。
    【解决方案2】:

    TypeReference 抽象类并不意味着直接用作通用信息的容器。这是来自javadoc

    这个泛型抽象类用于获取完整的泛型类型 按子分类的信息;它必须转换为 ResolvedType 实现(由“databind”包中的 JavaType 实现)为 用过。

    即使 ObjectMapper 接受了参数化的 TypeReference ,它也无法完成这项工作,因为像 new TypeReference&lt;List&lt;Long&gt;&gt; { } 这样的所有通用规范在运行时都会被完全删除。

    要将通用信息传递给杰克逊的ObjectMapper,您需要创建一个JavaType 实例:

    JavaType type = mapper.getTypeFactory().constructCollectionType(List.class, Long.class);
    

    然后

    List<Long> list = mapper.readValue(..., type);
    

    【讨论】:

    • T in Class&lt;T&gt; 也在运行时被删除(但确实,它是一个 JVM final 类,您不能将其子类化以创建一个提供不同类型信息的实现运行时,而不是在其通用参数中编码)。无论如何,这并不能回答我的问题,为什么readValue(String, TR) 采用原始TypeReferenceconvertValue 采用TypeReference&lt;?&gt;readValue(JsonParser, TR) 实际上采用TypeReference&lt;T&gt;...
    • 不管怎样,问题“one can build eg. new TypeReference> { } 告诉 ObjectMapper 输入需要反序列化为 long 列表”中的这句话是 FALSE
    • 怎么回事? List&lt;Long&gt; value = new ObjectMapper().&lt;List&lt;Long&gt;&gt;readValue("[1, 2, 3]", new TypeReference&lt;List&lt;Long&gt;&gt;() { }); 为我工作。它没有告诉 Jackson 如何构造这种类型(而且我对 Jackson 的了解还不够,无法告诉它实际上如何决定如何实例化它),但它似乎很好地告诉了它需要构造哪些类型。
    • 其他更复杂的类型也可以使用,例如。 readValue(str, new TypeReference&lt;List&lt;EventDTO&gt;&gt;() { }) 其中EventDTO 是一个数据类,其属性注释为@JsonProperty
    • 请了解类型标记。这是一个blog post,详细解释了它们。 @silmeth 就在这里:TypeReference 是一个类型标记,它包含有关某些泛型类型的信息。不过,我不知道他们为什么使用原始版本。对我来说似乎是一个错误,拥有非泛型类型令牌没有任何意义......
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-10-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-06-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多