自定义 Jackson 注解与禁用某一特定的注解
Jackson 是 Playfrmework 2 中默认的 JSON 处理框架,先前是 GSON,JSON 是 Playframework 中的第一等公民,可见 Jackson 在 Playframewok 中的重要地位。Jackson 提供了一系列的注解可用,像 @JsonIgnore, @JsonProperty, @JsonUnwrapped, @JsonFilter 等。人的需求总是很难得到满足,所以免不了还是要定义自己的注解。比如有这样一个需求,JavaBean 中被 @MaskField(这个即将成为我第一个自定义的注解) 标记的属性或 getter 方法,总是输出为
再次重复需求,对于下面的 Person 类生成的对象
想要对于某些用户生成
{"name":"Yanbin","age":"******","city":"Chicago","phone":"******"}
其他用户生成
{"name":"Yanbin","age":100,"city":"Chicago"}
即有条件的隐藏某些敏感信息。
希望自定义 @MaskField 来标 name 属性和 getPhone() 方法。
定义 @MaskField
上面指示了标记为 @MaskField 字段或 getter 方法奖应用 MaskFieldSerializer 为序列化,总是输出为 ******
MaskFieldSerializer 类
现在把 @MaskField 应用到 Person 类
用代码测试一下
输出结果没错了,就是
{"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
如果是 MaskField 就 return false, 禁用 @MaskField 注解
该告诉你的 ObjectMapper 使用这个自定义 AnnotationIntrospector,下面的测试代码
输出为
{"name":"Yanbin","age":100,"city":"Chicago"}
也就是要不要采用 DisablingMaskFieldIntrospector 就决定了我们禁止还是启用 @MaskField 注解的功能。
浪费点篇幅,上面完整的代码如下
执行后输出为
{"name":"Yanbin","age":"******","city":"Chicago","phone":"******"}
{"name":"Yanbin","age":100,"city":"Chicago"}
实际的操作就是根据不同的条件使用 maskMapper 或是 showAllMapper 来进行序列化。
参考:1. https://github.com/swagger-api/swagger-core/issues/982
2. http://stackoverflow.com/questions/12921812/create-a-custom-jackson-annotation 永久链接 https://yanbin.blog/customize-jackson-annotation-and-disable-specific-annotation/, 来自 隔叶黄莺 Yanbin's Blog
[版权声明]
本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。
******, 无此标记的属性或方法输出原始值。我尝试过 @JsonFilter 或是单纯的自定义 JsonSerializer, 并不怎么如意。本人最终的实现方式涉及到- @JacksonAnnotationsInside -- 用来创建自己的 @MaskField 注解
- JsonSerializer -- 被 @MaskField 标记的字段采用自定义的 JsonSerializer 来序列化
- JacksonAnnotationIntrospector -- 禁用某一特定的注解,这样可以在做任意时候启用或禁用 @MaskField
再次重复需求,对于下面的 Person 类生成的对象
1class Person {
2
3 public String name = "Yanbin";
4
5 public String getPhone () {
6 return "(312)666-8888";
7 }
8
9 public int age = 100;
10
11 @JsonProperty("city")
12 public String location = "Chicago";
13
14}想要对于某些用户生成
{"name":"Yanbin","age":"******","city":"Chicago","phone":"******"}
其他用户生成
{"name":"Yanbin","age":100,"city":"Chicago"}
即有条件的隐藏某些敏感信息。
希望自定义 @MaskField 来标 name 属性和 getPhone() 方法。
定义 @MaskField
1@Retention(RetentionPolicy.RUNTIME)
2@Target({ElementType.FIELD, ElementType.METHOD})
3@JacksonAnnotationsInside
4@JsonSerialize(using = MaskFieldSerializer.class)
5@interface MaskField {
6}上面指示了标记为 @MaskField 字段或 getter 方法奖应用 MaskFieldSerializer 为序列化,总是输出为 ******
MaskFieldSerializer 类
1class MaskFieldSerializer extends JsonSerializer<Object> {
2
3 @Override
4 public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider)
5 throws IOException {
6 jgen.writeString("******");
7 }
8}现在把 @MaskField 应用到 Person 类
1class Person {
2
3 public String name = "Yanbin";
4
5 @MaskField
6 public String phone () {
7 return "(312)666-8888";
8 }
9
10 @MaskField
11 public int age = 100;
12
13 @JsonProperty("city")
14 public String location = "Chicago";
15
16}用代码测试一下
1ObjectMapper maskMapper = new ObjectMapper();
2System.out.println(maskMapper.writeValueAsString(new Person()));输出结果没错了,就是
{"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
1class DisablingMaskFieldIntrospector extends JacksonAnnotationIntrospector {
2
3 @Override
4 public boolean isAnnotationBundle(Annotation ann) {
5 if (ann.annotationType().equals(MaskField.class)) {
6 return false;
7 } else {
8 return super.isAnnotationBundle(ann);
9 }
10 }
11
12}如果是 MaskField 就 return false, 禁用 @MaskField 注解
该告诉你的 ObjectMapper 使用这个自定义 AnnotationIntrospector,下面的测试代码
1ObjectMapper showAllMapper = new ObjectMapper();
2showAllMapper.setAnnotationIntrospector(new DisablingMaskFieldIntrospector());
3System.out.println(showAllMapper.writeValueAsString(new Person()));输出为
{"name":"Yanbin","age":100,"city":"Chicago"}
也就是要不要采用 DisablingMaskFieldIntrospector 就决定了我们禁止还是启用 @MaskField 注解的功能。
浪费点篇幅,上面完整的代码如下
1import com.fasterxml.jackson.annotation.*;
2import com.fasterxml.jackson.core.*;
3import com.fasterxml.jackson.databind.*;
4import com.fasterxml.jackson.databind.annotation.JsonSerialize;
5import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector;
6
7import java.io.IOException;
8import java.lang.annotation.*;
9
10public class TestJacksonAnnotation {
11
12 private static ObjectMapper maskMapper = new ObjectMapper();
13
14 private static ObjectMapper showAllMapper = new ObjectMapper() {{
15 setAnnotationIntrospector(new DisablingMaskFieldIntrospector());
16 }};
17
18 public static void main(String[] args) throws JsonProcessingException {
19 System.out.println(maskMapper.writeValueAsString(new Person()));
20 System.out.println(showAllMapper.writeValueAsString(new Person()));
21 }
22}
23
24class Person {
25
26 public String name = "Yanbin";
27
28 @MaskField
29 public String phone () {
30 return "(312)666-8888";
31 }
32
33 @MaskField
34 public int age = 100;
35
36 @JsonProperty("city")
37 public String location = "Chicago";
38
39}
40
41class MaskFieldSerializer extends JsonSerializer<Object> {
42
43 @Override
44 public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider)
45 throws IOException {
46 jgen.writeString("******");
47 }
48}
49
50@Retention(RetentionPolicy.RUNTIME)
51@Target({ElementType.FIELD, ElementType.METHOD})
52@JacksonAnnotationsInside
53@JsonSerialize(using = MaskFieldSerializer.class)
54@interface MaskField {
55
56}
57
58class DisablingMaskFieldIntrospector extends JacksonAnnotationIntrospector {
59
60 @Override
61 public boolean isAnnotationBundle(Annotation ann) {
62 if (ann.annotationType().equals(MaskField.class)) {
63 return false;
64 } else {
65 return super.isAnnotationBundle(ann);
66 }
67 }
68
69}执行后输出为
{"name":"Yanbin","age":"******","city":"Chicago","phone":"******"}
{"name":"Yanbin","age":100,"city":"Chicago"}
实际的操作就是根据不同的条件使用 maskMapper 或是 showAllMapper 来进行序列化。
参考:1. https://github.com/swagger-api/swagger-core/issues/982
2. http://stackoverflow.com/questions/12921812/create-a-custom-jackson-annotation 永久链接 https://yanbin.blog/customize-jackson-annotation-and-disable-specific-annotation/, 来自 隔叶黄莺 Yanbin's Blog
[版权声明]
本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。