一、问题的提出。 

项目使用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. 【等价的穿越】Burnside引理&Pólya计数法

    Problem 起源: SGU 294 He's Circle 遗憾的是,被吃了. Poj有道类似的: Mission 一个长度为n(1≤n≤24)的环由0,1,2组成,求有多少本质不同的环. 实际上 ...

  2. 【JZOJ3297】【SDOI2013】逃考(escape)

    Mission 高考又来了,对于不认真读书的来讲真不是个好消息.为了小杨能在家里认真读书,他的亲戚决定驻扎在他的家里监督他学习,有爷爷奶奶.外公外婆.大舅.大嫂.阿姨-- 小杨实在是忍无可忍了,这种生 ...

  3. elipse egit的使用

  4. Leetcode717.1-bit and 2-bit Characters1比特与2比特字符

    有两种特殊字符.第一种字符可以用一比特0来表示.第二种字符可以用两比特(10 或 11)来表示. 现给一个由若干比特组成的字符串.问最后一个字符是否必定为一个一比特字符.给定的字符串总是由0结束. 示 ...

  5. Liferay 7 OSGi第三方jar包依赖问题

    发现遇到这个问题的人挺多的,现在跟新一下好好写. 在开发Liferay的时候,我们常常会遇到Unresolved requirement: Import-Package: {package name} ...

  6. 为什么学习React Native三点原因

    React Native不到两岁,兼容Android平台刚刚1年.我学习React Native其实也就不到1年,不算长,也不算短. Paul Graham在文章中写过:大多数人真正注意到你的时候,不 ...

  7. Hibernate→ 《Hibernate程序开发》教材大纲

    Hibernate ORM 概览 Hibernate 简介 Hibernate 架构 Hibernate 环境 Hibernate 配置 Hibernate 会话 Hibernate 持久化类 Hib ...

  8. 洛谷 P3258 [JLOI2014]松鼠的新家 树链剖分+差分前缀和优化

    目录 题面 题目链接 题目描述 输入输出格式 输入格式 输出格式 输入输出样例 输入样例: 输出样例: 说明 说明 思路 AC代码 优化 优化后AC代码 总结 题面 题目链接 P3258 [JLOI2 ...

  9. ZOJ 3956 Course Selection System [01背包]

    http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3956 题意:就是给你Hi,Ci的值,问怎么取使得下面那个式子的值最大: 理 ...

  10. UVa 825【简单dp,递推】

    UVa 825 题意:给定一个网格图(街道图),其中有一些交叉路口点不能走.问从西北角走到东南角最短走法有多少种.(好像没看到给数据范围...) 简单的递推吧,当然也就是最简单的动归了.显然最短路长度 ...