自定义 Jackson 注解与禁用某一特定的注解

来源:互联网 发布:程序员都用什么电脑 编辑:程序博客网 时间:2024/05/29 04:17

非常感谢  http://unmi.cc/customize-jackson-annotation-and-disable-specific-annotation/


一、jackson
jackson可以轻松的将Java对象转换成json对象和xml文档,同样也可以将json、xml转换成Java对象。最新的版本到2.8,稳定版为2.7
从Jackson 2.0起,核心组件包括:
jackson-annotations(带注释的数据绑定包)。
jackson-core(流式的解析器和生成器)。
jackson-databind(对象映射器,JSON树模型)。
1、java对象转json[json序列化]
     看demo
2、json转java类[json反序列化]
     看demo
3、json注解
Jackson提供了一系列注解,方便对JSON序列化和反序列化进行控制,下面介绍一些常用的注解。
  • @JsonIgnore 此注解用于属性上,作用是进行JSON操作时忽略该属性。
  • @JsonFormat 此注解用于属性上,作用是把Date类型直接转化为想要的格式,如@JsonFormat(pattern = "yyyy-MM-dd HH-mm-ss")。
  • @JsonProperty 此注解用于属性上,作用是把该属性的名称序列化为另外一个名称,如把trueName属性序列化name,JsonProperty("name")。
  • @JsonIgnoreProperties({"name","age"}),作用在类上,用来说明属性在序列化和反序列化时需要忽略掉。
  • @JsonIgnoreProperties(ignoreUnknown=true)也可以注明在反序列化的时候过滤掉未知的属性,防止报错。
  • @JsonUnwrapped作用在属性上,用来将子Json对象的属性添加到Json对象。
  • @JsonUnwrapped作用在属性上,2.0+版本中添加了prefix和suffix属性,用在字段添加前后缀。
  • @JsonNaming作用在类或方法上,作用和JsonProperty名称相同,同时也支持我们自己定制属性命名的策略。
  • @JsonSerializer(using=MyDateSerializer.class),来指定序列化的实现,一般用于各种自定义的格式转换。
  • @JsonDeserializer(using=MyDateDeserializer.class),来指定反序列化的实现,一般用于格式的转换。
  • @JsonPropertyOrder(alphabetic=true,value={"c","b"}),序列化时对属性是否按字母顺序排序,默认不排序,value中的是优先其他排序的属性名称。
  • 。。。。。很多灵活好用的注解,很强大


Jackson 是 Playfrmework 2 中默认的 JSON 处理框架,先前是  GSON,JSON 是 Playframework 中的第一等公民,可见 Jackson 在 Playframewok 中的重要地位。Jackson 提供了一系列的注解可用,像 @JsonIgnore, @JsonProperty, @JsonUnwrapped, @JsonFilter 等。人的需求总是很难得到满足,所以免不了还是要定义自己的注解。比如有这样一个需求,JavaBean 中被 @MaskField(这个即将成为我第一个自定义的注解) 标记的属性或 getter 方法,总是输出为 ******, 无此标记的属性或方法输出原始值。

我尝试过 @JsonFilter  或是单纯的自定义  JsonSerializer, 并不怎么如意。本人最终的实现方式涉及到

  • @JacksonAnnotationsInside -- 用来创建自己的 @MaskField 注解
  • JsonSerializer  -- 被 @MaskField 标记的字段采用自定义的 JsonSerializer 来序列化
  • JacksonAnnotationIntrospector  -- 禁用某一特定的注解,这样可以在做任意时候启用或禁用 @MaskField

再次重复需求,对于下面的 Person 类生成的对象

classPerson {   publicString name  = "Yanbin";   publicString getPhone () {    return"(312)666-8888";  }   publicint age = 100;   @JsonProperty("city")  publicString location = "Chicago"; }

想要对于某些用户生成

{"name":"Yanbin","age":"******","city":"Chicago","phone":"******"}

其他用户生成

{"name":"Yanbin","age":100,"city":"Chicago"}

即有条件的隐藏某些敏感信息。

希望自定义 @MaskField 来标 name 属性和 getPhone() 方法。

定义 @MaskField

@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.FIELD, ElementType.METHOD})@JacksonAnnotationsInside@JsonSerialize(using = MaskFieldSerializer.class)@interfaceMaskField {}

上面指示了标记为 @MaskField 字段或 getter 方法奖应用  MaskFieldSerializer 为序列化,总是输出为 ******

MaskFieldSerializer 类

classMaskFieldSerializer extendsJsonSerializer<Object> {   @Override  publicvoid serialize(Object value, JsonGenerator jgen, SerializerProvider provider)    throwsIOException {    jgen.writeString("******");  }}

现在把 @MaskField 应用到  Person 类

classPerson {   publicString name  = "Yanbin";   @MaskField  publicString phone () {    return"(312)666-8888";  }   @MaskField  publicint age = 100;   @JsonProperty("city")  publicString location = "Chicago"; }

用代码测试一下

  ObjectMapper maskMapper = newObjectMapper();  System.out.println(maskMapper.writeValueAsString(newPerson()));

输出结果没错了,就是

{"name":"Yanbin","age":"******","city":"Chicago","phone":"******"}

本文原始链接, 来自 g

凡是标记了 @MaskField 的字段或 getter 方法最终输出统统为 ******, 还记得还有个需求是对于某些时候还希望看到原貌,即{"name":"Yanbin","age":100,"city":"Chicago"}

也就是有时要只禁用 @MaskField 注解。Jackson 可以调用

maskMapper.configure(MapperFeature.USE_ANNOTATIONS, false);

来禁用所有的注解,那么像 @JsonProperty, @JsonIgnore 这样的注解如何是好,肯定不能一棍子打死,所以这里只是需要适时的把  @MaskField 禁用即可。这时候要用到 JacksonAnnotationIntrospector 来选择某个 Annotation 来禁用了。

因而自定义 DisablingMaskFieldIntrospector

classDisablingMaskFieldIntrospector extendsJacksonAnnotationIntrospector {   @Override  publicboolean isAnnotationBundle(Annotation ann) {    if(ann.annotationType().equals(MaskField.class)) {      returnfalse;    }else{      returnsuper.isAnnotationBundle(ann);    }  } }

如果是 MaskField 就 return false, 禁用  @MaskField 注解

该告诉你的 ObjectMapper 使用这个自定义 AnnotationIntrospector,下面的测试代码

ObjectMapper showAllMapper = newObjectMapper();showAllMapper.setAnnotationIntrospector(newDisablingMaskFieldIntrospector());System.out.println(showAllMapper.writeValueAsString(newPerson()));

输出为

{"name":"Yanbin","age":100,"city":"Chicago"}

也就是要不要采用 DisablingMaskFieldIntrospector 就决定了我们禁止还是启用 @MaskField 注解的功能。


浪费点篇幅,上面完整的代码如下

importcom.fasterxml.jackson.annotation.*;importcom.fasterxml.jackson.core.*;importcom.fasterxml.jackson.databind.*;importcom.fasterxml.jackson.databind.annotation.JsonSerialize;importcom.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector; importjava.io.IOException;importjava.lang.annotation.*; publicclass TestJacksonAnnotation {   privatestatic ObjectMapper maskMapper = newObjectMapper();   privatestatic ObjectMapper showAllMapper = newObjectMapper() {{    setAnnotationIntrospector(newDisablingMaskFieldIntrospector());  }};   publicstatic void main(String[] args) throwsJsonProcessingException {    System.out.println(maskMapper.writeValueAsString(newPerson()));    System.out.println(showAllMapper.writeValueAsString(newPerson()));  }} classPerson {   publicString name  = "Yanbin";   @MaskField  publicString phone () {    return"(312)666-8888";  }   @MaskField  publicint age = 100;   @JsonProperty("city")  publicString location = "Chicago"; } classMaskFieldSerializer extendsJsonSerializer<Object> {   @Override  publicvoid serialize(Object value, JsonGenerator jgen, SerializerProvider provider)    throwsIOException {    jgen.writeString("******");  }} @Retention(RetentionPolicy.RUNTIME)@Target({ElementType.FIELD, ElementType.METHOD})@JacksonAnnotationsInside@JsonSerialize(using = MaskFieldSerializer.class)@interfaceMaskField { } classDisablingMaskFieldIntrospector extendsJacksonAnnotationIntrospector {   @Override  publicboolean isAnnotationBundle(Annotation ann) {    if(ann.annotationType().equals(MaskField.class)) {      returnfalse;    }else{      returnsuper.isAnnotationBundle(ann);    }  } }

执行后输出为

{"name":"Yanbin","age":"******","city":"Chicago","phone":"******"}
{"name":"Yanbin","age":100,"city":"Chicago"}

实际的操作就是根据不同的条件使用  maskMapper 或是 showAllMapper 来进行序列化。

参考:1. https://github.com/swagger-api/swagger-core/issues/982


0 0