【问题标题】:Does Jackson TypeReference work when extended?Jackson TypeReference 在扩展时是否有效?
【发布时间】:2013-10-09 22:01:30
【问题描述】:

下面的 sn-p 是不言自明的。可以看到类型信息没有被擦除,但是mapper没有得到类型信息。我的猜测是杰克逊不允许这样做,对吧?如果我直接通过 TypeReference,它会被正确反序列化。

public class AgentReq<T> extends TypeReference<AgentResponse<T>> {...}

mapper.readValue(reader, new AgentReq<Map<String, Set<Whatever>>>());

如果我这样做,它也不起作用:

public class AgentReq<T> {

    public TypeReference<AgentResponse<T>> getTypeRef() {
        return new TypeReference<AgentResponse<T>>() {};
    }
}

mapper.readValue(reader, new AgentReq<Map<String, Set<Whatever>>>()).getTypeRef();

我使用的是 2.1.5 版。

编辑:为了将来参考,解决问题时不要低估 TypeReference 构造函数。在那里你可以直接看到它是否能够检索类型信息。顺便说一句,答案是否定的,你不能扩展 TypeReference 并期望它工作,你甚至不能覆盖它的 getType() 方法并提供从你的类解析的类型信息,因为你能得到的只是 getClass()。 getGenericSuperClass() ... 你不能做 getClass().getGenericClass()

【问题讨论】:

  • 你能提供一个使用TypeReference的类吗?小型 SSCCE?
  • 现在是凌晨 1 点 35 分,我要睡觉了 Sotirios……我明天早上再看看。
  • NP,我回家也试试。

标签: java json jackson


【解决方案1】:

您需要了解TypeReference 的工作原理。为此,我们进入源代码

protected TypeReference()
{
    Type superClass = getClass().getGenericSuperclass();
    if (superClass instanceof Class<?>) { // sanity check, should never happen
        throw new IllegalArgumentException("Internal error: TypeReference constructed without actual type information");
    }
    ...
    _type = ((ParameterizedType) superClass).getActualTypeArguments()[0];
}

Class#getGenericSuperclass() javadoc 状态

返回表示实体的直接超类的类型 (类、接口、原始类型或 void)由该类表示。

如果超类是参数化类型,则返回 Type 对象 必须准确反映源中使用的实际类型参数 代码。

换句话说,如果我们可以做new TypeReference()(我们做不到,它是抽象的),它将返回类ObjectClass 实例。但是,对于匿名类(从类型扩展)

new TypeReference<String>(){}

创建的实例的直接超类是参数化类型TypeReference,根据javadoc,我们应该得到一个Type 实例,该实例准确反映了源代码中使用的实际类型参数

TypeReference<String>

然后您可以从中获取带有getActualTypeArguments()[0]) 的参数化类型,返回String

让我们举个例子来可视化使用匿名类和使用子类

public class Subclass<T> extends TypeReference<AgentResponse<T>>{
    public Subclass() {
        System.out.println(getClass().getGenericSuperclass());
        System.out.println(((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0]);
    }
}

跑步

new Subclass<String>();

打印

com.fasterxml.jackson.core.type.TypeReference<Test.AgentResponse<T>>
Test.AgentResponse<T>

符合 javadoc 规则。 Test.AgentResponse&lt;T&gt; 是源代码中实际的参数化类型。现在,如果相反,我们有

new Subclass<String>(){}; // anonymous inner class

我们得到结果

Test.Subclass<java.lang.String>
class java.lang.String

这也符合要求。内部类现在直接从 Subclass 扩展而来,在源代码中使用参数 String 进行参数化。

您会注意到,对于 Subclass 匿名内部类,我们丢失了有关 AgentResponse 泛型类型的信息。这是不可避免的。


注意

reader = new StringReader("{\"element\":{\"map-element\":[{\"name\":\"soto\", \"value\": 123}]}}");
obj = mapper.readValue(reader, new AgentReq<Map<String, Set<Whatever>>>());

将编译并运行,但类型AgentReq&lt;Map&lt;String, Set&lt;Whatever&gt;&gt;&gt; 将丢失。 Jackson 将使用默认类型来序列化 JSON。 element 将反序列化为AgentResponse,而map-element 将反序列化为Map,JSON 数组将反序列化为ArrayList

【讨论】:

  • 我低估了 TypeReference 构造函数,正如您所描述的,所有重要的事情都会发生,并且可以看到,除非您直接将 TypeReference 抽象类实例化为匿名类,否则它没有机会解析类型信息。谢谢
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-09-11
相关资源
最近更新 更多