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

非常感谢  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


  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值