自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Jackson注解的用法和場(chǎng)景,不看巨虧

開發(fā) 前端
@JacksonAnnotationsInside這個(gè)注解用來(lái)標(biāo)記Jackson復(fù)合注解,當(dāng)你使用多個(gè)Jackson注解組合成一個(gè)自定義注解時(shí)會(huì)用到它。

[[411385]]

Jackson注解一覽

今天總結(jié)一下Jackson的一系列注解的用法和場(chǎng)景,或許能幫助你實(shí)現(xiàn)一些功能,總結(jié)不易,還請(qǐng)多多關(guān)注、點(diǎn)贊、轉(zhuǎn)發(fā)。

@JacksonAnnotation

這個(gè)注解經(jīng)常用于Jackson自定義注解中,用來(lái)標(biāo)記這是一個(gè)Jackson注解,這個(gè)胖哥在Jackson脫敏一文中用過它來(lái)實(shí)現(xiàn)自定義的序列化注解。

@JacksonAnnotationsInside

這個(gè)注解用來(lái)標(biāo)記Jackson復(fù)合注解,當(dāng)你使用多個(gè)Jackson注解組合成一個(gè)自定義注解時(shí)會(huì)用到它。

  1. /** 
  2.  * 非空以及忽略未知屬性 
  3.  **/ 
  4. @Retention(RetentionPolicy.RUNTIME) 
  5. @JacksonAnnotationsInside 
  6. @JsonInclude(Include.NON_NULL) 
  7. @JsonIgnoreProperties(ignoreUnknown = true
  8. public @interface NotNullAndIgnoreAnnotation {} 

@JacksonInject

json屬性值將在反序列化時(shí)可以被注入,我們先在屬性上標(biāo)記:

  1. @Data 
  2. public final class JacksonInjectUser { 
  3.     @JacksonInject(value = "dynamic"
  4.     private String name
  5.     private Integer age; 

然后name的值就可以在反序列化的時(shí)候動(dòng)態(tài)化,不再需要去解析、拼字段。

  1. @SneakyThrows 
  2. @Test 
  3. void jacksonInject() { 
  4.     // 這個(gè)值動(dòng)態(tài)化了 
  5.     String dynamicValue = "some Dynamic value"
  6.     InjectableValues.Std injectableValues = new InjectableValues.Std() 
  7.             // 名稱和注解中聲明的相同才行 
  8.             .addValue("dynamic", dynamicValue); 
  9.     JacksonInjectUser jacksonInjectUser = objectMapper.setInjectableValues(injectableValues) 
  10.             // 空json 最后居然可以賦值 
  11.             .readValue("{}", JacksonInjectUser.class); 
  12.     Assertions.assertEquals(dynamicValue,jacksonInjectUser.getName()); 

注意:@JacksonInject中提供了useInput參數(shù)進(jìn)行綁定策略控制。

@JsonAlias

在反序列化的時(shí)候來(lái)對(duì)Java Bean的屬性進(jìn)行名稱綁定,可以綁定多個(gè)json的鍵名。舉個(gè)例子:

  1.     @SneakyThrows 
  2.     @Test 
  3.     void jsonAlias(){ 
  4.         // 兩個(gè)json的類型結(jié)構(gòu)是相同的 可以定義一個(gè)Bean來(lái)接收 
  5.         String userJson = "{\"name\": \"felord.cn\",\"age\": 22}"
  6.         String itemJson = "{\"category\": \"coco\", \"count\": 50 }"
  7.         Domain user = objectMapper.readValue(userJson, Domain.class); 
  8.         Assertions.assertEquals("felord.cn",user.getStr()); 
  9.         Assertions.assertEquals(22,user.getNum()); 
  10.         Domain item = objectMapper.readValue(itemJson, Domain.class); 
  11.         Assertions.assertEquals("coco",item.getStr()); 
  12.         Assertions.assertEquals(50,item.getNum()); 
  13.     } 
  14.  
  15. @Data 
  16. public class Domain{ 
  17.     @JsonAlias({"name","category"}) 
  18.     private String str; 
  19.     @JsonAlias({"age","count"}) 
  20.     private Integer num; 

注意:只能用于json反序列化。

@JsonAnyGetter

在json序列化時(shí)可以將Bean中的java.util.Map類型的屬性“平鋪展開”,舉個(gè)例子:

某個(gè)Java Bean正常的json序列化結(jié)果是:

  1.   "name""felord.cn"
  2.   "age": 22, 
  3.   "unMatched": { 
  4.     "unknown""unknown" 
  5.   } 

但是我們需要:

  1.   "name""felord.cn"
  2.   "age": 22, 
  3.   "unknown""unknown" 

我們可以對(duì)Java Bean這么標(biāo)記:

  1. @Data 
  2. public class MapUser { 
  3.     private String name
  4.     private Integer age; 
  5.     private Map<String,Object> unMatched; 
  6.  
  7.     @JsonAnyGetter 
  8.     public Map<String, Object> getUnMatched() { 
  9.         return unMatched; 
  10.     } 

然后我們來(lái)試一試:

  1. @SneakyThrows 
  2. @Test 
  3. void jsonAnyGetter(){ 
  4.     MapUser mapUser = new MapUser(); 
  5.  
  6.     mapUser.setName("felord.cn"); 
  7.     mapUser.setAge(22); 
  8.     mapUser.setUnMatched(Collections.singletonMap("unknown","unknown")); 
  9.  
  10.     String json = objectMapper.writeValueAsString(mapUser); 
  11.     // 獲取json中unknown節(jié)點(diǎn)的值 
  12.     Object read = JsonPath.parse(json) 
  13.             .read(JsonPath.compile("$.unknown")); 
  14.     Assertions.assertEquals("unknown",read); 

不過這個(gè)注解的使用也是有條件的:

  • 不能是靜態(tài)方法。
  • 必須是無(wú)參方法。
  • 方法的返回值必須是java.util.Map。
  • 一個(gè)實(shí)體中只能使用一個(gè)該注解。

@JsonAnySetter

正好和@JsonAnyGetter相反,這里就不介紹了。

@JsonAutoDetect

一般情況下,我們認(rèn)為Jackson序列化對(duì)象的前提是有無(wú)參構(gòu)造并且有Getter方法。事實(shí)上下面這個(gè)類依然可以序列化成json:

  1. @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY
  2. public class ConstructUser { 
  3.     private final String name
  4.     private final Integer age; 
  5.  
  6.     public ConstructUser(String nameInteger age) { 
  7.         this.name = name
  8.         this.age = age; 
  9.     } 

我們可以通過調(diào)整Java Bean中屬性、getter方法、isGetter方法、setter方法、初始化實(shí)例的方法??梢娂?jí)別可以分為:

  • DEFAULT: 需要根據(jù)上下文來(lái)判斷,一般基于父類的可見性。
  • ANY:任何級(jí)別的都可以自動(dòng)識(shí)別。
  • NONE:所有級(jí)別都不可以自動(dòng)識(shí)別。
  • NON_PRIVATE:非private修飾的可以自動(dòng)識(shí)別。
  • PROTECTED_AND_PUBLIC:被protected和public修飾的可以被自動(dòng)識(shí)別。
  • PUBLIC_ONLY:只有被public修飾的才可以被自動(dòng)識(shí)別。

@JsonBackReference

這個(gè)注解經(jīng)常和另一個(gè)注解@JsonManagedReference成對(duì)出現(xiàn),它為了解決遞歸的問題,例如兩個(gè)類互相持有對(duì)方:

  1. Info info = new Info(); 
  2. Player player = new Player(); 
  3. player.setId(1); 
  4. info.setPlayer(player); 
  5. player.setInfo(info); 
  6. // 直接無(wú)限遞歸了 
  7. String InfiniteRecursionError = objectMapper.writeValueAsString(player); 

json序列化的時(shí)候直接無(wú)限遞歸了。如果你想得到下面的序列化結(jié)果:

  1. // player 
  2. {"id":1,"info":{"id":0}} 

就需要在類Player的Info屬性上標(biāo)記@JsonManagedReference,同時(shí)在Info類中的Player屬性上標(biāo)記@JsonBackReference注解。

如果你想在序列化Player時(shí)直接忽略掉Info屬性,即期望得到{"id":1},只需要在Player的Info屬性上標(biāo)記@JsonBackReference注解。

@JsonClassDescription

Jackson對(duì)json schemas的支持,用來(lái)生成整個(gè)json的描述信息。

@JsonCreator

Jackson在反序列化時(shí)默認(rèn)會(huì)去找Java Bean的無(wú)參構(gòu)造,但是有些Bean沒有無(wú)參構(gòu)造,這時(shí)@JsonCreator就派上用場(chǎng)了。你可以將它標(biāo)記在構(gòu)造方法或靜態(tài)工廠方法上,通常它還需要同@JsonProperty或@JacksonInject配合,就像這樣:

  1. @Getter 
  2. public class DescriptionUser { 
  3.     private final String name
  4.     private final Integer age; 
  5.  
  6.     @JsonCreator 
  7.     public DescriptionUser(@JsonProperty("name") String name,  
  8.                            @JsonProperty("age"Integer age) { 
  9.         this.name = name
  10.         this.age = age; 
  11.     } 

對(duì)應(yīng)的單元測(cè)試:

  1. @SneakyThrows 
  2. @Test 
  3. void jsonCreator() { 
  4.     String json = "{\"name\": \"felord.cn\",\"age\": 22}"
  5.     DescriptionUser user = objectMapper.readValue(json, DescriptionUser.class); 
  6.     Assertions.assertEquals("felord.cn"user.getName()); 

你可以在靜態(tài)初始化實(shí)例工廠方法上試試這個(gè)注解。

@JsonEnumDefaultValue

我們?cè)诙x性別枚舉時(shí)往往只定義了男和女兩個(gè)性別。你不能指望用戶守規(guī)矩??茖W(xué)的方法是定義一個(gè)枚舉用來(lái)兜底。就像這樣:

  1. public enum Gender { 
  2.     /** 
  3.      * Female gender. 
  4.      */ 
  5.     FEMALE, 
  6.     /** 
  7.      * Male gender. 
  8.      */ 
  9.     MALE, 
  10.     /** 
  11.      * Unknown gender. 
  12.      */ 
  13.     UNKNOWN 
  14.    } 

當(dāng)用戶亂填的時(shí)候都定義為未知。在jackson反序列化支持設(shè)置一個(gè)默認(rèn)值來(lái)兜底。我們可以在Gender#UNKNOWN上標(biāo)記@JsonEnumDefaultValue,然后反序列化:

  1. @SneakyThrows 
  2. @Test 
  3. void jsonEnumDefaultValue(){ 
  4.     // 開啟未知枚舉值使用默認(rèn)值特性 
  5.     objectMapper.enable(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE); 
  6.     String maleJson = "{\"name\": \"felord.cn\",\"age\": 22,\"gender\":\"MALE\"}"
  7.  
  8.     EnumUser male = objectMapper.readValue(maleJson, EnumUser.class); 
  9.     Assertions.assertEquals(Gender.MALE,male.getGender()); 
  10.  
  11.     String unknownJson = "{\"name\": \"felord.cn\",\"age\": 22,\"gender\":\"notClear\"}"
  12.     EnumUser unknownGender = objectMapper.readValue(unknownJson, EnumUser.class); 
  13.     Assertions.assertEquals(Gender.UNKNOWN,unknownGender.getGender()); 

注意:必須手動(dòng)jackson開啟未知枚舉值使用默認(rèn)值特性。

@JsonFilter

同一個(gè)實(shí)體類根據(jù)不同的場(chǎng)景可能需要不同的序列化策略。比如對(duì)于A用戶實(shí)體的某些字段可見,對(duì)于B用戶另一些字段可見,實(shí)現(xiàn)動(dòng)態(tài)的數(shù)據(jù)字段權(quán)限。這種情況下,jackson中其它一些靜態(tài)注解就很難實(shí)現(xiàn),借助于@JsonFilter反而簡(jiǎn)單了,下面是實(shí)現(xiàn)方法:

  1. @JsonFilter("role_a"
  2. public class OnlyAge extends FilterUser{ 
  3. // 不序列化age的策略  
  4. @JsonFilter("role_b"
  5. public class OnlyNameAndGender extends FilterUser{ 

接下來(lái)定義role_a和role_b的策略:

  1. @SneakyThrows 
  2. @Test 
  3. void jsonFilter() { 
  4.     SimpleFilterProvider simpleFilterProvider = new SimpleFilterProvider(); 
  5.     // role_a只展示age   
  6.     SimpleBeanPropertyFilter onlyAgeFilter = SimpleBeanPropertyFilter.filterOutAllExcept("age"); 
  7.     // role_b只排除age 
  8.     SimpleBeanPropertyFilter exceptAgeFilter = SimpleBeanPropertyFilter.serializeAllExcept("age"); 
  9.     simpleFilterProvider.addFilter("role_a", onlyAgeFilter); 
  10.     simpleFilterProvider.addFilter("role_b", exceptAgeFilter); 
  11.     objectMapper.setFilterProvider(simpleFilterProvider); 
  12.  
  13.     //被JsonFilter標(biāo)記的類 
  14.     OnlyAge onlyAgeUser = new OnlyAge(); 
  15.     onlyAgeUser.setName("felord.cn"); 
  16.     onlyAgeUser.setGender(Gender.MALE); 
  17.     onlyAgeUser.setAge(22); 
  18.  
  19.     OnlyNameAndGender onlyNameAndGenderUser = new OnlyNameAndGender(); 
  20.     onlyNameAndGenderUser.setName("felord.cn"); 
  21.     onlyNameAndGenderUser.setGender(Gender.MALE); 
  22.     onlyNameAndGenderUser.setAge(22); 
  23.  
  24.     String onlyAge = objectMapper.writeValueAsString(onlyAgeUser); 
  25.     // 序列化的json中找不到name節(jié)點(diǎn)會(huì)拋出PathNotFoundException異常 
  26.     Assertions.assertThrows(PathNotFoundException.class, () -> JsonPath.parse(onlyAge) 
  27.             .read(JsonPath.compile("$.name"))); 
  28.     String onlyNameAndGender = objectMapper.writeValueAsString(onlyNameAndGenderUser); 
  29.     // 序列化的json中找不到age節(jié)點(diǎn)會(huì)拋出PathNotFoundException異常 
  30.     Assertions.assertThrows(PathNotFoundException.class, () -> JsonPath.parse(onlyNameAndGender) 
  31.             .read(JsonPath.compile("$.age"))); 

思考:結(jié)合AOP甚至是Spring Security是不是有搞頭?

小結(jié)

Jackson是一款非常優(yōu)秀的json類庫(kù),提供了豐富的注解來(lái)滿足各種場(chǎng)景的需要。本篇介紹了一部分注解的用法和場(chǎng)景。胖哥也根據(jù)日常一些場(chǎng)景的需要結(jié)合這些注解設(shè)計(jì)了不少動(dòng)態(tài)的、可擴(kuò)展的、通用的序列化和反序列化功能,用起來(lái)非常方便順手。只有掌握了技術(shù)才能運(yùn)用技術(shù),后續(xù)計(jì)劃把剩下所有的注解都梳理出來(lái)分享給大家。另外keycloak的教程也在準(zhǔn)備中,還請(qǐng)多多關(guān)注和支持。

本文轉(zhuǎn)載自微信公眾號(hào)「碼農(nóng)小胖哥」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系碼農(nóng)小胖哥公眾號(hào)。

 

責(zé)任編輯:武曉燕 來(lái)源: 碼農(nóng)小胖哥
相關(guān)推薦

2013-01-24 13:39:32

中興華為企業(yè)運(yùn)維

2011-03-09 13:03:05

諾西摩托羅拉企業(yè)并購(gòu)

2012-03-05 09:37:43

2023-03-27 22:16:03

2009-03-18 08:41:54

日立巨虧總裁

2009-03-18 08:29:23

NVIDIA上網(wǎng)本移動(dòng)OS

2024-08-05 10:01:54

VR/AR

2023-08-26 11:36:31

Java框架Spring

2024-05-07 08:23:03

Spring@Async配置

2015-08-04 13:14:20

賺錢虧錢大數(shù)據(jù)

2025-01-13 12:46:31

SpringBootJacksonJSON

2023-09-28 09:07:54

注解失效場(chǎng)景

2020-12-02 16:13:30

比特幣投資

2019-12-10 15:30:27

SaaSIaaS云計(jì)算

2022-02-19 07:41:36

Bean注解項(xiàng)目

2022-02-20 07:28:13

Spring注解用法

2021-12-30 12:30:01

Java注解編譯器

2023-07-26 00:32:33

注解抽象spring

2012-02-09 10:42:24

Java
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)