SpringMvc @JsonView 使用方式
准确来说,@JsonView注解不是Spring的,它位于jackson-annotation包中; 作用:SpringMvc使用@ResponseBody将结果以json返回客户端, 有些实体类的某些字段可以不被包括在JSON中;
思考了这种情况可能适用的情形:有多方调用这个接口,需要针对不同业务场景返回不同形式的JSON,但是这种情况的话 只拷贝需要的属性 再返回也能达到目的,就当多学了一种注解、处理方式了;
一.作为 Jackson Api使用:
public class View { public static class UserPublic{} public static class UserPrivacy{} }
public class User { @JsonView(View.UserPublic.class) public int id; @JsonView(View.UserPrivacy.class) public String name; }
补充说明:每一个@JsonView标注在属性上,代表该属性在被序列化时候会被转化,视图名字为value指向的class;
public class User { @JsonView(View.UserPublic.class) int id; @JsonView(View.UserPrivacy.class) String name; public void setId(int id) { this.id = id; } public void setName(String name) { this.name = name; } } public static void main(String[] args) throws JsonProcessingException { User user = new User(); user.setId(1); user.setName("lvbb"); ObjectMapper mapper = new ObjectMapper(); String res1 = mapper.writerWithView(View.UserPublic.class).writeValueAsString(user); System.out.println(res1); String res2 = mapper.writerWithView(View.UserPrivacy.class).writeValueAsString(user); System.out.println(res2); }
输出结果:
@JsonView注解没有@Repeatable注解标注,意味着一个属性只能标注一个@JsonView注解, 比如 View.UserPrivacy 这个Json View也需要有id字段,解决方案就是 让 UserPrivacy 继承 UserPublic视图
同样的main方法查看输出: {"id":1,"name":"lvbb"}
二. 结合Spring的使用方式. (注解@JsonView的 value只能为一个)
简单列举个例子如下: 在@ResponseBody标注下添加 @JsonView指定要展示的JSON View即可.
@RequestMapping("/demo2") @ResponseBody @JsonView(value = MyView.UserDetail.class) public User demo2(){ User user = new User(); user.setName("lvbinbin"); user.setPassword("qwer0-5"); return user; }
上面的代码使用效果和下面一段是一模一样的:
@RequestMapping(value = "/demo4") @ResponseBody public MappingJacksonValue demo4(){ User user = new User(); user.setName("lvbinbin"); user.setPassword("qwer0-5"); MappingJacksonValue mjv=new MappingJacksonValue(user); mjv.setSerializationView(MyView.UserDetail.class); return mjv; }
Json View Api使用:
User user = new User(); user.setId(1); user.setName("lvbb"); ObjectMapper mapper = new ObjectMapper(); MappingJacksonValue mappingJacksonValue = new MappingJacksonValue(user); mappingJacksonValue.setSerializationView(View.UserPrivacy.class); ObjectWriter writer = mapper.writerWithView(mappingJacksonValue.getSerializationView()); String res4= writer.writeValueAsString(mappingJacksonValue.getValue()); System.out.println(res4);
当然MappingJacksonValue好像也是支持SpringMvc跨域JSONP调用的,具体不太了解以后补上.
同样类似的,接收JSON请求参数使用 @RequestBody同样可以和 @JsonView使用,这时候前台接收到参数就可以忽略指定的字段
@RequestMapping("/demo5") @ResponseBody public String demo5(@RequestBody @JsonView(MyView.UserSimple.class) User user){ System.out.println(user); return "Hello World"; }
Spring底层对MappingJacksonValue以及 @JsonView 流程记录:
MappingJackson的处理过程如下
org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter#writeInternal 有这样一个代码片段:
@ResponseBody的返回值类型是MappingJacksonValue类型时,会做的多一步转换工作,取出mappingJackson的value值.
org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor#writeWithMessageConverters
在往响应对象Response写回结果之前, 会调用ResponseBodyAdvice接口的beforeBodyWrite方法进行切面处理,当前对象为 RequestResponseBodyAdviceChain,其持有JsonViewResponseBodyAdvice 以及JsonViewRequestBodyAdvice, 前者就是处理@ResponseBody的;
JsonViewResponseBodyAdvice 直接获取了 方法上的JsonView注解,然后调用Jackson View Api进行设置;也就是说上面提及到的两种方式是一模一样的.
参考文档: