Spring/json:转换类型化集合,例如 List

发布于 2024-11-10 13:36:59 字数 1172 浏览 2 评论 0 原文

我正在尝试通过 Spring Rest 模板编组一个列表:List 对象。

我可以传递简单的 Pojo 对象,但我找不到任何描述如何发送 List 对象的文档。

Spring 使用 Jackson JSON 来实现 HttpMessageConverter 。杰克逊文档涵盖了这一点:

除了绑定到 POJO 之外, “简单”类型,有一种 附加变体:绑定到 通用(类型化)容器。这个案例 需要特殊处理,因为 所谓的类型擦除(Java 使用 在某种程度上实现泛型 向后兼容的方式),其中 阻止你使用类似的东西 Collection.class (这确实 未编译)。

所以如果你想将数据绑定到 Map 您需要使用:

地图<字符串,用户> result = mapper.readValue(src, new TypeReference>() {});

其中 TypeReference 只需要 传递泛型类型定义(通过 在这种情况下是任意内部类): 重要的部分是 > 定义类型 绑定到。

这个可以在Spring模板中完成吗?我看了一眼代码,它让我觉得不舒服,但也许我只是不知道一些技巧。


解决方案

感谢下面的有用答案,最终的解决方案是不发送 List,而是发送一个简单扩展 List 的单个对象,例如: class PojoList extends ArrayList ;。 Spring 可以成功地编组这个对象,并且它完成与发送 List 相同的事情,尽管它是一个不太干净的解决方案。我还在 Spring 发布了一个 JIRA,以解决他们的 HttpMessageConverter 接口中的这​​个缺点。

I'm trying to marshal a list: List<Pojo> objects via the Spring Rest Template.

I can pass along simple Pojo objects, but I can't find any documentation that describes how to send a List<Pojo> objects.

Spring is using Jackson JSON to implement the HttpMessageConverter. The jackson documentation covers this:

In addition to binding to POJOs and
"simple" types, there is one
additional variant: that of binding to
generic (typed) containers. This case
requires special handling due to
so-called Type Erasure (used by Java
to implement generics in somewhat
backwards compatible way), which
prevents you from using something like
Collection<String>.class (which does
not compile).

So if you want to bind data into a
Map<String,User> you will need to use:

Map<String,User> result = mapper.readValue(src, new TypeReference<Map<String,User>>() {});

where TypeReference is only needed to
pass generic type definition (via
anynomous inner class in this case):
the important part is
<Map<String,User>> which defines type
to bind to.

Can this be accomplished in the Spring template? I took a glance at the code and it makes me thing not, but maybe I just don't know some trick.


Solution

The ultimate solution, thanks to the helpful answers below, was to not send a List, but rather send a single object which simply extends a List, such as: class PojoList extends ArrayList<Pojo>. Spring can successfully marshal this Object, and it accomplishes the same thing as sending a List<Pojo>, though it be a little less clean of a solution. I also posted a JIRA in spring for them to address this shortcoming in their HttpMessageConverter interface.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

旧梦荧光笔 2024-11-17 13:36:59

Spring 3.2 中,现在使用 RestTemplate 上的新 exchange() 方法支持泛型类型:

 ParameterizedTypeReference<List<MyBean>> typeRef = new ParameterizedTypeReference<List<MyBean>>() {};
 ResponseEntity<List<MyBean>> response = template.exchange("http://example.com", HttpMethod.GET, null, typeRef);

非常有用!

In Spring 3.2 there is now support for generic types using the new exchange()-methods on the RestTemplate:

 ParameterizedTypeReference<List<MyBean>> typeRef = new ParameterizedTypeReference<List<MyBean>>() {};
 ResponseEntity<List<MyBean>> response = template.exchange("http://example.com", HttpMethod.GET, null, typeRef);

Works like a charm!

债姬 2024-11-17 13:36:59

确保包含泛型类型参数的一种方法是实际上对 List 或 Map 类型进行子类化,这样您就有类似:

static class MyStringList extends ArrayList<String> { }

并返回该列表的实例。

那么为什么这会产生影响呢?因为泛型类型信息只保留在几个地方:方法和字段声明以及超类型声明。因此,虽然“原始”列表不包含任何运行时类型信息,但“MyStringList”的类定义通过其超类型声明包含任何运行时类型信息。
请注意,对看似类型化的变量进行赋值并没有帮助:它只会创建更多编译时语法糖:真正的类型信息仅通过 Class 实例(或其 lib 提供的扩展,例如 Jackson 中的 JavaType 和 TypeReference)传递。

除此之外,您还需要弄清楚如何传递 Jackson JavaTypeTypeReference 来伴随值。

One way to ensure that generic type parameters are included is to actually sub-class List or Map type, such that you have something like:

static class MyStringList extends ArrayList<String> { }

and return instance of that list.

So why does this make a difference? Because generic type information is retained in just a couple of places: method and field declarations, and super type declarations. So whereas "raw" List does NOT include any runtime type information, class definition of "MyStringList" does, through its supertype declarations.
Note that assignments to seemingly typed variables do not help: it just creates more compile-time syntactic sugar: real type information is only passed with Class instances (or lib-provided extensions thereof, like JavaType and TypeReference in Jackson's case).

Other than this, you would need to figure out how to pass Jackson either JavaType or TypeReference to accompany value.

杀お生予夺 2024-11-17 13:36:59

如果我阅读 MappingJacksonHttpMessageConverter 的文档正确,您必须创建并注册 MappingJacksonHttpMessageConverter 的子类并覆盖 getJavaType(类)方法:

返回 Jackson JavaType
具体类。默认实现
回报
TypeFactory.type(java.lang.reflect.Type),
但这可以被覆盖
子类,允许自定义
通用集合处理。为了
实例:

protected JavaType getJavaType(Class<?> clazz) {
   if (List.class.isAssignableFrom(clazz)) {
     return TypeFactory.collectionType(ArrayList.class, MyBean.class);
   } else {
     return super.getJavaType(clazz);
   }
}

If I read the docs for MappingJacksonHttpMessageConverter right, you will have to create and register a subclass of MappingJacksonHttpMessageConverter and override the getJavaType(Class<?>) method:

Returns the Jackson JavaType for the
specific class. Default implementation
returns
TypeFactory.type(java.lang.reflect.Type),
but this can be overridden in
subclasses, to allow for custom
generic collection handling. For
instance:

protected JavaType getJavaType(Class<?> clazz) {
   if (List.class.isAssignableFrom(clazz)) {
     return TypeFactory.collectionType(ArrayList.class, MyBean.class);
   } else {
     return super.getJavaType(clazz);
   }
}
讽刺将军 2024-11-17 13:36:59

我通过使用以下配置解决了这个问题:

private static final String POJO_ARRAY_LIST = PojoArrayList.class.getCanonicalName();

@Bean
public HttpMessageConverter<Object> httpMessageConverter() {
    HttpMessageConverter<Object> httpMessageConverter = new MappingJackson2HttpMessageConverter() {
        @Override
        protected JavaType getJavaType(Type type, @Nullable Class<?> contextClass) {
            JavaType javaType;
            if (type != null && POJO_ARRAY_LIST.equals(type.getTypeName())) {
                ObjectMapper objectMapper = new ObjectMapper();
                TypeFactory typeFactory = objectMapper.getTypeFactory();
                CollectionType collectionType = typeFactory.constructCollectionType(ArrayList.class, Pojo.class);
                javaType = collectionType;
            } else {
                javaType = super.getJavaType(type, contextClass);
            }
            return javaType;
        }
    };
    return httpMessageConverter;
}

其中 PojoArrayList 是扩展 ArrayList 的最终类。

I have solved this problem by using the following configuration:

private static final String POJO_ARRAY_LIST = PojoArrayList.class.getCanonicalName();

@Bean
public HttpMessageConverter<Object> httpMessageConverter() {
    HttpMessageConverter<Object> httpMessageConverter = new MappingJackson2HttpMessageConverter() {
        @Override
        protected JavaType getJavaType(Type type, @Nullable Class<?> contextClass) {
            JavaType javaType;
            if (type != null && POJO_ARRAY_LIST.equals(type.getTypeName())) {
                ObjectMapper objectMapper = new ObjectMapper();
                TypeFactory typeFactory = objectMapper.getTypeFactory();
                CollectionType collectionType = typeFactory.constructCollectionType(ArrayList.class, Pojo.class);
                javaType = collectionType;
            } else {
                javaType = super.getJavaType(type, contextClass);
            }
            return javaType;
        }
    };
    return httpMessageConverter;
}

where PojoArrayList is a final class that extends ArrayList<Pojo>.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文