一、问题的提出。 

项目使用Spring MVC框架,并用jackson库处理JSON和POJO的转换。在POJO转化成JSON时,希望动态的过滤掉对象的某些属性。所谓动态,是指的运行时,不同的controler方法可以针对同一POJO过滤掉不同的属性。 

以下是一个Controler方法的定义,使用@ResponseBody把获得的对象列表写入响应的输出流(当然,必须配置jackson的MappingJacksonHttpMessageConverter,来完成对象的序列化)

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定义

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 
二、使用annotation mixin动态过滤

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的定义如下: 

这个实现方法看起来非常不简洁,需要在动态过滤的时候写不少代码,而且也改变了@ResponseBody的运行方式,失去了REST风格,因此考虑到使用AOP来进行处理。 

二、最终解决方案 

先看下我想达到的目标,通过自定义注解的方式来控制动态过滤。

@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定义了混合的模板以及目标类。

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如下:

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工具类:

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。 

四、总结 

设计的一些要点: 

1、要便于程序员使用。程序员根据业务逻辑需要过滤字段时,只需要定义个"Filter“,然后使用注解引入该Filter。 

2、引入AOP来保持原来的REST风格。对于项目遗留的代码,不需要进行大幅度的修改,只需要增加注解来增加对过滤字段的支持。 

仍需解决的问题: 

按照目前的设计,定义的Filter不支持继承,每一种动态字段的业务需求就会产生一个Filter类,当类数量很多时,不便于管理。 

五、参考资料 

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

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

原文地址:https://www.cnblogs.com/zcw-ios/articles/3343071.html

java spring使用Jackson过滤的更多相关文章

  1. [Java] Spring + SpringMVC + Maven + JUnit 搭建

    示例项目下载: https://github.com/yangyxd/SpringDemo 利用前面 SpringMVC 项目的配置方式,完成初步的项目创建.下面只讲一些不同之处. 传送门: [Jav ...

  2. Java Spring Boot VS .NetCore (二)实现一个过滤器Filter

    Java Spring Boot VS .NetCore (一)来一个简单的 Hello World Java Spring Boot VS .NetCore (二)实现一个过滤器Filter Jav ...

  3. Java Spring Boot VS .NetCore (十) Java Interceptor vs .NetCore Interceptor

    Java Spring Boot VS .NetCore (一)来一个简单的 Hello World Java Spring Boot VS .NetCore (二)实现一个过滤器Filter Jav ...

  4. Jackson 过滤属性

    jackson过滤属性分为静态和动态两种. 静态如下: 定义两个Bean 先,这两个bean 是父子关系. public class User { private String name; priva ...

  5. Spring Security 多过滤链的使用

    Spring Security 多过滤链的使用 一.背景 二.需求 1.给客户端使用的api 2.给网站使用的api 三.实现方案 方案一: 方案二 四.实现 1.app 端 Spring Secur ...

  6. 从零开始学 Java - Spring 集成 Memcached 缓存配置(二)

    Memcached 客户端选择 上一篇文章 从零开始学 Java - Spring 集成 Memcached 缓存配置(一)中我们讲到这篇要谈客户端的选择,在 Java 中一般常用的有三个: Memc ...

  7. 从零开始学 Java - Spring 集成 ActiveMQ 配置(一)

    你家小区下面有没有快递柜 近两年来,我们收取快递的方式好像变了,变得我们其实并不需要见到快递小哥也能拿到自己的快递了.对,我说的就是类似快递柜.菜鸟驿站这类的代收点的出现,把我们原来快递小哥必须拿着快 ...

  8. 从零开始学 Java - Spring 集成 Memcached 缓存配置(一)

    硬盘和内存的作用是什么 硬盘的作用毫无疑问我们大家都清楚,不就是用来存储数据文件的么?如照片.视频.各种文档或等等,肯定也有你喜欢的某位岛国老师的动作片,这个时候无论我们电脑是否关机重启它们永远在那里 ...

  9. 从零开始学 Java - Spring 集成 ActiveMQ 配置(二)

    从上一篇开始说起 上一篇从零开始学 Java - Spring 集成 ActiveMQ 配置(一)文章中讲了我关于消息队列的思考过程,现在这一篇会讲到 ActivMQ 与 Spring 框架的整合配置 ...

随机推荐

  1. HTML之meta标签缓存控制

    <meta http-equiv="pragram" content="no-cache"> 禁止浏览器从本地缓存中调阅页面. 网页不保存在缓存中, ...

  2. GDSOI2017第三轮模拟4.21 总结

    1 第一题看着就觉得猎奇,于是就想着打暴力就跑. 但是很严重的问题就是... \(D\)和\(B\)打反了,都不知道当时在干什么??? 原本可以拿35. 2 第二题看着就觉得套路,于是想着今天就攻这题 ...

  3. DirectX11笔记(四)--渲染管线

    原文:DirectX11笔记(四)--渲染管线 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/u010333737/article/details/ ...

  4. C++11中的并发

    在 C++98 的时代,C++标准并没有包含多线程的支持,人们只能直接调用操作系统提供的 SDK API 来编写多线程程序,不同的操作系统提供的 SDK API 以及线程控制能力不尽相同.到了 C++ ...

  5. bnd -buildpath指令的用法

    -buildpath的作用是为项目添加运行时依赖.这个依赖可以是workspace中的另一个项目或者是仓库中的另一个bundle. -buildpath指令只会在编译和构建时起作用,它从来不会被用来运 ...

  6. Find Minimumd in Rotated Sorted Array

    二分搜索查最小数,from mid to分别为区间的第一个,中位数,和最后一个数 if(from<=mid&&mid<=to)//顺序,第一个即为最小值 return fr ...

  7. 【JZOJ4859】【NOIP2016提高A组集训第7场11.4】连锁店

    题目描述 Dpstr开了个饮料连锁店,连锁店共有n家,出售的饮料种类相同.为了促销,Dpstr决定让每家连锁店开展赠送活动.具体来说,在第i家店,顾客可以用ai个饮料瓶兑换到bi瓶饮料和1个纪念币(注 ...

  8. 解决eclipse不会自动弹出Console控制台的问题

    有时候Eclipse启动,控制台console不会自动跳出来,需要手工点击该选项卡才行, 解决方案: windows  ->   preferences   ->  run/debug   ...

  9. ffmpeg在iOS的使用 - iFrameExtractor源码解析

    http://www.cocoachina.com/ios/20150914/13284.html iFrameExtractor地址:https://github.com/lajos/iFrameE ...

  10. 洛谷 P4114 Qtree1 树链剖分

    目录 题面 题目链接 题目描述 输入输出格式 输入格式: 输出格式: 输入输出样例 输入样例: 输出样例: 说明 说明 思路 Change Query AC代码 总结 题面 题目链接 P4114 Qt ...