在泛型方法中传递类型时,使用 TypeReference 的 ObjectMapper 不起作用
ObjectMapper using TypeReference not working when passed type in generic method
这是方法:
protected <T> TestPageResult<T> getTestPageResutForRequest(MockHttpServletRequestBuilder request) throws Exception {
String responseJson = mockMvc.perform(request).andReturn().getResponse()
.getContentAsString();
TestPageResult<T> response = getObjectMapper().readValue(responseJson,
new TypeReference<TestPageResult<T>>() {
});
return response;
}
我这样称呼它:
TestPageResult<SomeDto> pageResult = this.<SomeDto>getTestPageResutForRequest(getRequest());
测试页面结果为:
protected static class TestPageResult<T> {
private List<T> items;
private long totalCount = -1;
public TestPageResult() {
}
//omitted getters and setters
}
生成的 pageResult.getItems() 包含一个 LinkedHashMap 列表,而不是一个 SomeDto 列表。如果我只是在 objectMapper.readValue 方法中硬编码 SomeDto 类型,我会得到正确的结果。
有什么问题?
编辑:建议的重复确实解决了我的问题 - 有点。
我用过:
JavaType type = getObjectMapper().getTypeFactory().constructParametricType(TestPageResult.class, clazz);
TestPageResult<T> response = getObjectMapper().readValue(responseJson, type);
问题是没有办法不向方法传递 Class 参数。因此该方法看起来很难看,因为它既传递了泛型类型又传递了与 Class 相同的东西。显然你现在不能通过泛型,但这样就需要转换并添加 SuppressWarnings 等等。
问题是擦除。所有这些 <T>
参数在被 擦除 之后都不存在于编译代码中。这意味着 source new TypeReference<TestPageResult<T>>()
编译后看起来像 new TypeReference<TestPageResult>()
,这不是您想要的。 (类似于 List<String>
如何在编译代码中最终成为 List
,这只是 compile-time 验证您没有将 Integer
添加到 String
List
.)
我认为大致有两种方法可以解决这个问题(在本例中),您已经偶然发现了这两种方法:
- 您可以创建一个可以正确表示您想要的类型,例如:
new TypeReference<TestPageResult<SomeDto>>()
,或者 class SomeDtoPageResult extends TestPageResult<SomeDto>
,然后您可以在 readValue(..., SomeDtoPageResult.class)
;[=35= 等地方使用它]
- 或者您创建一个完整的 class 表示,就像您对
JavaType
所做的那样
你真正想要的是行不通的。你最好的选择是修补并提出解决它的最干净的代码。泛型让你表达非常复杂的结构,当你序列化一个实际的实例(嵌套对象)时,结果很好,但是当 classes 需要在运行时进行自省时,例如对于反序列化(您的用例)或构建模型(例如生成 Swagger 文档),这会成为问题。
这是方法:
protected <T> TestPageResult<T> getTestPageResutForRequest(MockHttpServletRequestBuilder request) throws Exception {
String responseJson = mockMvc.perform(request).andReturn().getResponse()
.getContentAsString();
TestPageResult<T> response = getObjectMapper().readValue(responseJson,
new TypeReference<TestPageResult<T>>() {
});
return response;
}
我这样称呼它:
TestPageResult<SomeDto> pageResult = this.<SomeDto>getTestPageResutForRequest(getRequest());
测试页面结果为:
protected static class TestPageResult<T> {
private List<T> items;
private long totalCount = -1;
public TestPageResult() {
}
//omitted getters and setters
}
生成的 pageResult.getItems() 包含一个 LinkedHashMap 列表,而不是一个 SomeDto 列表。如果我只是在 objectMapper.readValue 方法中硬编码 SomeDto 类型,我会得到正确的结果。
有什么问题?
编辑:建议的重复确实解决了我的问题 - 有点。 我用过:
JavaType type = getObjectMapper().getTypeFactory().constructParametricType(TestPageResult.class, clazz);
TestPageResult<T> response = getObjectMapper().readValue(responseJson, type);
问题是没有办法不向方法传递 Class 参数。因此该方法看起来很难看,因为它既传递了泛型类型又传递了与 Class 相同的东西。显然你现在不能通过泛型,但这样就需要转换并添加 SuppressWarnings 等等。
问题是擦除。所有这些 <T>
参数在被 擦除 之后都不存在于编译代码中。这意味着 source new TypeReference<TestPageResult<T>>()
编译后看起来像 new TypeReference<TestPageResult>()
,这不是您想要的。 (类似于 List<String>
如何在编译代码中最终成为 List
,这只是 compile-time 验证您没有将 Integer
添加到 String
List
.)
我认为大致有两种方法可以解决这个问题(在本例中),您已经偶然发现了这两种方法:
- 您可以创建一个可以正确表示您想要的类型,例如:
new TypeReference<TestPageResult<SomeDto>>()
,或者class SomeDtoPageResult extends TestPageResult<SomeDto>
,然后您可以在readValue(..., SomeDtoPageResult.class)
;[=35= 等地方使用它] - 或者您创建一个完整的 class 表示,就像您对
JavaType
所做的那样
你真正想要的是行不通的。你最好的选择是修补并提出解决它的最干净的代码。泛型让你表达非常复杂的结构,当你序列化一个实际的实例(嵌套对象)时,结果很好,但是当 classes 需要在运行时进行自省时,例如对于反序列化(您的用例)或构建模型(例如生成 Swagger 文档),这会成为问题。