非常感谢 http://unmi.cc/customize-jackson-annotation-and-disable-specific-annotation/
- @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":"******"}
凡是标记了 @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