一、问题的提出。 

项目使用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. 2017年Android SDK下载安装及配置教程(附带原文地址)

    首先声明: Unity版本5.6.3f1 最近试着在Unity中利用高通做AR开发时,发布项目文件需要发布到Android平台,遇到一些问题,看了网上的一些资料,踩了一些坑,现在总结出来,希望有相同的 ...

  2. eNSP模拟器路由器无法正常启动一直显示“#”——问题解决方法

    eNSP模拟器路由器无法正常启动一直显示“#” 问题项如截图: 解决方案: 1. 打开自己电脑的控制面板 -->> 系统和安全 -->> Windows Defender防火墙 ...

  3. 洛谷P1063 能量项链 [2006NOIP提高组]

    P1063 能量项链 题目描述 在Mars星球上,每个Mars人都随身佩带着一串能量项链.在项链上有N颗能量珠.能量珠是一颗有头标记与尾标 记的珠子,这些标记对应着某个正整数.并且,对于相邻的两颗珠子 ...

  4. Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- Direct12优化

    原文:Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- Direct12优化 第一章:向量代数 1.向量计算的时候,使用XMV ...

  5. oracle终止数据库Abort

    中止数据库实例, 立即关闭 异常关闭是最主动的关闭类型,并且有如下这些特征: 从shutdown abort命令发布起,禁止建立任何新的oracle连接 当前正在运行的sql语句被终止,无论他们处于什 ...

  6. 【Mysql的那些事】数据库之ORM操作

    1:ORM的基础操作(必会) <1> all(): 查询所有结果 <2> filter(**kwargs): 它包含了与所给筛选条件相匹配的对象 <3> get(* ...

  7. spark编译与onyarn的执行

    版权声明:本文为博主原创文章.未经博主同意不得转载. https://blog.csdn.net/u014393917/article/details/24640715 Spark on yarn执行 ...

  8. c++ 模板和traits

    #define TEST(ITEMNAME) AddItem(ITEMNAME, #ITEMNAME); template <typename T> void AddItem(T& ...

  9. Spring Security 4 使用@PreAuthorize,@PostAuthorize, @Secured, EL实现方法安全

    [相关已翻译的本系列其他文章,点击分类里面的spring security 4] 上一篇:Spring Security 4 整合Hibernate 实现持久化登录验证(带源码) 原文地址:http: ...

  10. 2018-9-19-Roslyn-通过-Nuget-管理公司配置

    title author date CreateTime categories Roslyn 通过 Nuget 管理公司配置 lindexi 2018-9-19 10:57:5 +0800 2018- ...