在之前类似的问题中,我问过如何使用JacksonJson和Spring序列化两组不同的字段。
我的用例是典型的控制器映射与@ResponseBody
注释直接返回一个特定的对象或对象集合,然后呈现与JacksonJson每当客户端添加application/json
在Accept
头。
我有两个答案,第一个建议用不同的getter列表返回不同的接口,第二个建议使用Json视图。
我没有问题来理解第一种方式,然而,对于第二种方式,在阅读了JacksonJsonViews
的文档后,我不知道如何用Spring实现它。
为了保持示例,我将在类Views中声明三个stub类:
// View definitions:
public class Views {
public static class Public { }
public static class ExtendedPublic extends PublicView { }
public static class Internal extends ExtendedPublicView { }
}
然后我必须声明上面提到的类:
public class PublicView { }
public class ExtendedPublicView { }
为什么他们声明空静态类和外部空类,我不知道。我理解它们需要一个"标签",但是视图的静态成员就足够了。这并不是说ExtendedPublic
扩展了Public
,因为这是合乎逻辑的,但它们实际上是完全不相关的。
最后,bean将用注释指定视图或视图列表:
//changed other classes to String for simplicity and fixed typo
//in classname, the values are hardcoded, just for testing
public class Bean {
// Name is public
@JsonView(Views.Public.class)
String name = "just testing";
// Address semi-public
@JsonView(Views.ExtendedPublic.class)
String address = "address";
// SSN only for internal usage
@JsonView(Views.Internal.class)
String ssn = "32342342";
}
最后,在Spring Controller中,我必须考虑如何更改测试bean的原始映射:
@RequestMapping(value = "/bean")
@ResponseBody
public final Bean getBean() {
return new Bean();
}
它说调用:
//or, starting with 1.5, more convenient (ObjectWriter is reusable too)
objectMapper.viewWriter(ViewsPublic.class).writeValue(out, beanInstance);
所以我有一个ObjectMapper
实例无处不在,out
不是servlet典型的PrintWriter out = response.getWriter();
,而是JsonGenerator
的一个实例,不能用新的操作符获得。所以我不知道如何修改方法,这里是一个不完整的尝试:
@RequestMapping(value = "/bean")
@ResponseBody
public final Bean getBean() throws JsonGenerationException, JsonMappingException, IOException {
ObjectMapper objectMapper = new ObjectMapper();
JsonGenerator out; //how to create?
objectMapper.viewWriter(Views.Public.class).writeValue(out, new Bean());
return ??; //what should I return?
}
所以我想知道是否有人成功地使用JsonView
与Spring以及他/她是如何做到的。整个概念似乎很有趣,但似乎缺乏文档,也缺少示例代码。
如果不可能,我将使用接口相互扩展。不好意思,这个问题太长了
根据@igbopie和@chrislovecnm的回答,我整理了一个注释驱动的解决方案:
@Controller
public class BookService
{
@RequestMapping("/books")
@ResponseView(SummaryView.class)
public @ResponseBody List<Book> getBookSummaries() {}
@RequestMapping("/books/{bookId}")
public @ResponseBody Book getBook(@PathVariable("bookId") Long BookId) {}
}
其中SummaryView
在Book
模型上注释如下:
@Data
class Book extends BaseEntity
{
@JsonView(SummaryView.class)
private String title;
@JsonView(SummaryView.class)
private String author;
private String review;
public static interface SummaryView extends BaseView {}
}
@Data
public class BaseEntity
{
@JsonView(BaseView.class)
private Long id;
}
public interface BaseView {}
一个自定义的HandlerMethodReturnValueHandler
然后被连接到Spring MVC的上下文中来检测@ResponseView
注释,并相应地应用Jackson视图。
我已经在我的博客上提供了完整的代码
您需要手动连接MappingJacksonHttpMessageConverter。在spring 3.1中,您可以像下面这样使用mvc xml标签:
<mvc:annotation-driven >
<mvc:message-converter>
<bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter" />
</mvc:message-converters>
</mvc:annotation-driven>
不使用spring 3.1是非常难看的,它将为您节省大约20行xml。注释标签执行ALOT。
您需要将对象映射器与正确的视图写入器连接起来。我最近注意到,使用@Configuration类可以使像这样复杂的连接变得容易得多。使用@Configuration类并使用MappingJacksonHttpMessageConverter创建@Bean,并将引用连接到该bean,而不是上面的MappingJacksonHttpMessageConverter。
我已经设法用这种方法解决了这个问题:
- 创建包含json响应对象的自定义抽象类:
public abstract AbstractJson<E>{
@JsonView(Views.Public.class)
private E responseObject;
public E getResponseObject() {
return responseObject;
}
public void setResponseObject(E responseObject) {
this.responseObject = responseObject;
}
}
- 为每个可见性创建一个类(只是为了标记响应):
public class PublicJson<E> extends AbstractJson<E> {}
public class ExtendedPublicJson<E> extends AbstractJson<E> {}
public class InternalJson<E> extends AbstractJson<E> {}
- 更改方法声明:
@RequestMapping(value = "/bean")
@ResponseBody
public final PublicJson<Bean> getBean() throws JsonGenerationException, JsonMappingException, IOException {
return new PublicJson(new Bean());
}
- 创建自定义MessageConverter:
public class PublicJsonMessageConverter extends MappingJacksonHttpMessageConverter{
public PublicApiResponseMessageConverter(){
super();
org.codehaus.jackson.map.ObjectMapper objMapper=new org.codehaus.jackson.map.ObjectMapper();
objMapper.configure(SerializationConfig.Feature.DEFAULT_VIEW_INCLUSION, false);
objMapper.setSerializationConfig(objMapper.getSerializationConfig().withView(Views.Public.class));
this.setObjectMapper(objMapper);
}
public boolean canWrite(Class<?> clazz, MediaType mediaType) {
if(clazz.equals(PublicJson.class)){
return true;
}
return false;
}
}
public class ExtendedPublicJsonMessageConverter extends MappingJacksonHttpMessageConverter{
public ExtendedPublicJsonMessageConverter(){
super();
org.codehaus.jackson.map.ObjectMapper objMapper=new org.codehaus.jackson.map.ObjectMapper();
objMapper.configure(SerializationConfig.Feature.DEFAULT_VIEW_INCLUSION, false);
objMapper.setSerializationConfig(objMapper.getSerializationConfig().withView(Views.ExtendedPublic.class));
this.setObjectMapper(objMapper);
}
public boolean canWrite(Class<?> clazz, MediaType mediaType) {
if(clazz.equals(ExtendedPublicJson.class)){
return true;
}
return false;
}
}
public class InternalJsonMessageConverter extends MappingJacksonHttpMessageConverter{
public InternalJsonMessageConverter(){
super();
org.codehaus.jackson.map.ObjectMapper objMapper=new org.codehaus.jackson.map.ObjectMapper();
objMapper.configure(SerializationConfig.Feature.DEFAULT_VIEW_INCLUSION, false);
objMapper.setSerializationConfig(objMapper.getSerializationConfig().withView(Views.Internal.class));
this.setObjectMapper(objMapper);
}
public boolean canWrite(Class<?> clazz, MediaType mediaType) {
if(clazz.equals(Internal.class)){
return true;
}
return false;
}
}
- 在xml中添加以下内容:
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="PublicJsonMessageConverter"></bean>
<bean class="ExtendedPublicJsonMessageConverter"></bean>
<bean class="InternalJsonMessageConverter"></bean>
</mvc:message-converters>
</mvc:annotation-driven>
就是这样!我不得不升级到spring 3.1,但仅此而已。我使用responseObject来发送有关json调用的更多信息,但您可以覆盖MessageConverter的更多方法以完全透明。我希望有一天春天会包含一个注释。
希望这对你有帮助!