一、问题的提出。 

项目使用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. JPA 将驼峰列名自动转换为_

    数据库中和代码中都没有'cat_age'列名:但是用jpa保存的时候,总是提示此错误:这个问题纠结半天,后来在朋友的指点下,找到问题所在: spring data  jpa 使用默认策略是Improv ...

  2. 电影的微信小程序

    最近,工作没有那么忙,学习了一下小程序开发,感觉上手比较简单. 在项目中学习是最好的方式,于是就自己模仿豆瓣电影开发一款微信小程序版的豆瓣电影 准备工作: 数据来源:豆瓣电影API 功能: 电影榜单列 ...

  3. MySQL安装后设置root 密码

    Mysql安装完成后初始化root 密码为空,直接回车 使用命令行: mysqladmin -u root password "123456" 来设置root密码.这里我设置的密码 ...

  4. php的模板原理

    下载了开源论坛phpbb的代码,突然对php模板初步了解了一下: php与Html在一起编写真的是很烦人,所以必须要把数据计算以及显示格式分离,这就需要模板来实现了. http://baike.bai ...

  5. 2019-8-30-C#-从零开始写-SharpDx-应用-笔刷

    title author date CreateTime categories C# 从零开始写 SharpDx 应用 笔刷 lindexi 2019-8-30 8:50:0 +0800 2019-6 ...

  6. oracle-ORA-27102错误

    out of memory HP-UX Error: 12: Not enough space ORA-30019: Illegal rollback Segment operation in Aut ...

  7. Leetcode788.Rotated Digits旋转数字

    我们称一个数 X 为好数, 如果它的每位数字逐个地被旋转 180 度后,我们仍可以得到一个有效的,且和 X 不同的数.要求每位数字都要被旋转. 如果一个数的每位数字被旋转以后仍然还是一个数字, 则这个 ...

  8. JavaScript中操作数组的方法

    JavaScript Array 对象 对数组操作的方法分为两种 一种是会改变原始数组的变异方法,还有一种是不会改变原始数组的非变异方法. 总结 巧记 Push() 尾部添加 pop() 尾部删除 U ...

  9. MaxCompute 费用暴涨之存储压缩率降低导致SQL输入量变大

    现象:同样的SQL,每天处理的数据行数差不多,但是费用突然暴涨甚至会翻数倍. 分析: 我们先明确MaxCompute SQL后付费的计费公式:一条SQL执行的费用=扫描输入量 ️ SQL复杂度 ️ 0 ...

  10. @NOIP2018 - D2T3@ 保卫王国

    目录 @题目描述@ @题解@ @代码@ @题目描述@ Z 国有n座城市,n−1 条双向道路,每条双向道路连接两座城市,且任意两座城市 都能通过若干条道路相互到达. Z 国的国防部长小 Z 要在城市中驻 ...