java spring使用Jackson過濾

1、問題的提出。 

項目使用Spring MVC框架,並用jackson庫處理JSON和POJO的轉換。在POJO轉化成JSON時,但願動態的過濾掉對象的某些屬性。所謂動態,是指的運行時,不一樣的controler方法能夠針對同一POJO過濾掉不一樣的屬性。 

如下是一個Controler方法的定義,使用@ResponseBody把得到的對象列表寫入響應的輸出流(固然,必須配置jackson的MappingJacksonHttpMessageConverter,來完成對象的序列化)html

?
1
2
3
4
5
6
7
8
@RequestMapping (params = "method=getAllBmForList" )
@ResponseBody
public List<DepartGenInfo> getAllBmForList(HttpServletRequest request,
         HttpServletResponse response) throws Exception {
     
     BmDto dto = bmglService.getAllBm();
     return dto.getBmList();
}

POJO定義java

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class DepartGenInfo implements java.io.Serializable {
 
      private String depid;
      private String name;
      private Company company;
 
      //getter...
      //setter...
}
 
public class Company  {
 
      private String comid;
      private String name;
<pre name= "code" class = "java" >      //getter...
      //setter...
}

我但願在getAllBmForList返回時,過濾掉DepartGenInfo的name屬性,以及company的comid屬性。 

jackson支持@JsonIgnore和@JsonIgnoreProperties註解,可是沒法實現動態過濾。jackson給出了幾種動態過濾的辦法,我選擇使用annotation mixin 


•JSON View 
•JSON Filter 
•Annotation Mixin 
2、使用annotation mixin動態過濾ios

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@RequestMapping (params = "method=getAllBmForList" )
public void getAllBmForList(HttpServletRequest request,
         HttpServletResponse response) throws Exception {
     
     BmDto dto = bmglService.getAllBm();
    
     ObjectMapper mapper = new ObjectMapper();
     SerializationConfig serializationConfig = mapper.getSerializationConfig();
     serializationConfig.addMixInAnnotations(DepartGenInfo. class ,
       DepartGenInfoFilter. class );
 
     serializationConfig.addMixInAnnotations(Company. class ,
       CompanyFilter. class );
     
     mapper.writeValue(response.getOutputStream(),dto.getBmList());
     return ;
}

DepartGenInfoFilter的定義以下:程序員

?
@JsonIgnoreProperties (value={ "name" }) //但願動態過濾掉的屬性
public interface DepartGenInfoFilter {
} //CompanyFilter的定義以下: 
serializationConfig.addMixInAnnotations();  

這個實現方法看起來很是不簡潔,須要在動態過濾的時候寫很多代碼,並且也改變了@ResponseBody的運行方式,失去了REST風格,所以考慮到使用AOP來進行處理。 

2、最終解決方案 

先看下我想達到的目標,經過自定義註解的方式來控制動態過濾。web

?
@XunerJsonFilters (value={ @XunerJsonFilter (mixin=DepartGenInfoFilter. class , target=DepartGenInfo. class )
             , @XunerJsonFilter (mixin=CompanyFilter. class , target=Company. class )})
     @RequestMapping (params = "method=getAllBmForList" )
     @ResponseBody
     public List getAllBmForList(HttpServletRequest request,
             HttpServletResponse response) throws Exception {
         
         BmDto dto = bmglService.getAllBm();
return dto.getBmList();
     }

@XunerJsonFilters和@XunerJsonFilter是我定義的註解。@XunerJsonFilters是@XunerJsonFilter的集合,@XunerJsonFilter定義了混合的模板以及目標類。spring

?
1
2
3
4
5
6
7
8
9
@Retention (RetentionPolicy.RUNTIME)
public @interface XunerJsonFilters {
     XunerJsonFilter[] value();
}
@Retention (RetentionPolicy.RUNTIME)
public @interface XunerJsonFilter {
   Class<?> mixin() default Object. class ;
   Class<?> target() default Object. class ;
}

固然,只是定義註解並無什麼意義。重要的是如何根據自定義的註解進行處理。我定義了一個AOP Advice以下:express

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
public class XunerJsonFilterAdvice {
 
     public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
         MethodSignature msig = (MethodSignature) pjp.getSignature();
         XunerJsonFilter annotation = msig.getMethod().getAnnotation(
                 XunerJsonFilter. class );
         XunerJsonFilters annotations = msig.getMethod().getAnnotation(
                 XunerJsonFilters. class );
 
         if (annotation == null && annotations == null ) {
             return pjp.proceed();
         }
 
         ObjectMapper mapper = new ObjectMapper();
         if (annotation != null ) {
             Class<?> mixin = annotation.mixin();
             Class<?> target = annotation.target();
             
             if (target != null ) {
                 mapper.getSerializationConfig().addMixInAnnotations(target,
                         mixin);
             } else {
                 mapper.getSerializationConfig().addMixInAnnotations(
                         msig.getMethod().getReturnType(), mixin);
             }
         }
         
         if (annotations != null ) {
             XunerJsonFilter[] filters= annotations.value();
             for (XunerJsonFilter filter :filters){
                 Class<?> mixin = filter.mixin();
                 Class<?> target = filter.target();
                 
                 if (target != null ) {
                     mapper.getSerializationConfig().addMixInAnnotations(target,
                             mixin);
                 } else {
                     mapper.getSerializationConfig().addMixInAnnotations(
                             msig.getMethod().getReturnType(), mixin);
                 }
             }
             
         }
         
 
         try {
             mapper.writeValue(WebContext.getInstance().getResponse()
                     .getOutputStream(), pjp.proceed());
         } catch (Exception ex) {
             throw new RuntimeException(ex);
         }
         return null ;
     }
 
}

其中pointcut的expression可以匹配到目標類的方法。 

在doAround方法中,須要得到當前引用的HttpResponse對象,所以使用如下方法解決: 

建立一個WebContext工具類:json

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
public class WebContext {
 
     private static ThreadLocal<WebContext> tlv = new ThreadLocal<WebContext>();
     private HttpServletRequest request;
     private HttpServletResponse response;
     private ServletContext servletContext;
 
     protected WebContext() {
     }
 
     public HttpServletRequest getRequest() {
         return request;
     }
 
     public void setRequest(HttpServletRequest request) {
         this .request = request;
     }
 
     public HttpServletResponse getResponse() {
         return response;
     }
 
     public void setResponse(HttpServletResponse response) {
         this .response = response;
     }
 
     public ServletContext getServletContext() {
         return servletContext;
     }
 
     public void setServletContext(ServletContext servletContext) {
         this .servletContext = servletContext;
     }
 
     private WebContext(HttpServletRequest request,
             HttpServletResponse response, ServletContext servletContext) {
         this .request = request;
         this .response = response;
         this .servletContext = servletContext;
     }
 
     public static WebContext getInstance() {
         return tlv.get();
     }
 
     public static void create(HttpServletRequest request,
             HttpServletResponse response, ServletContext servletContext) {
         WebContext wc = new WebContext(request, response, servletContext);
         tlv.set(wc);
     }
 
     public static void clear() {
         tlv.set( null );
     }
}

別忘了在web.xml中增長這個filter。 




OK,It is all。 




4、總結 

設計的一些要點: 

一、要便於程序員使用。程序員根據業務邏輯須要過濾字段時,只須要定義個"Filter「,而後使用註解引入該Filter。 

二、引入AOP來保持原來的REST風格。對於項目遺留的代碼,不須要進行大幅度的修改,只須要增長註解來增長對過濾字段的支持。 

仍需解決的問題: 

按照目前的設計,定義的Filter不支持繼承,每一種動態字段的業務需求就會產生一個Filter類,當類數量不少時,不便於管理。 




5、參考資料 

http://www.cowtowncoder.com/blog/archives/cat_json.html 

http://www.jroller.com/RickHigh/entry/filtering_json_feeds_from_springapp

原文地址:https://www.cnblogs.com/zcw-ios/articles/3343071.html
相關文章
相關標籤/搜索