您现在的位置是:亿华云 > 数据库

Jackson注解的用法和场景,不看巨亏

亿华云2025-10-03 02:16:57【数据库】0人已围观

简介Jackson注解一览今天总结一下Jackson的一系列注解的用法和场景,或许能帮助你实现一些功能,总结不易,还请多多关注、点赞、转发。@JacksonAnnotation这个注解经常用于Jackso

Jackson注解一览

今天总结一下Jackson的不看巨亏一系列注解的用法和场景,或许能帮助你实现一些功能,解的景总结不易,用法还请多多关注、和场点赞、不看巨亏转发。解的景

@JacksonAnnotation

这个注解经常用于Jackson自定义注解中,用法用来标记这是和场一个Jackson注解,这个胖哥在Jackson脱敏一文中用过它来实现自定义的不看巨亏序列化注解。

@JacksonAnnotationsInside

这个注解用来标记Jackson复合注解,解的景当你使用多个Jackson注解组合成一个自定义注解时会用到它。

/**  * 非空以及忽略未知属性  **/ @Retention(RetentionPolicy.RUNTIME) @JacksonAnnotationsInside @JsonInclude(Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) public @interface NotNullAndIgnoreAnnotation { } 

@JacksonInject

json属性值将在反序列化时可以被注入,用法我们先在属性上标记:

@Data public final class JacksonInjectUser {      @JacksonInject(value = "dynamic")     private String name;     private Integer age; } 

然后name的和场值就可以在反序列化的时候动态化,不再需要去解析、不看巨亏拼字段。解的景

@SneakyThrows @Test void jacksonInject() {      // 这个值动态化了     String dynamicValue = "some Dynamic value";     InjectableValues.Std injectableValues = new InjectableValues.Std()             // 名称和注解中声明的用法相同才行             .addValue("dynamic", dynamicValue);     JacksonInjectUser jacksonInjectUser = objectMapper.setInjectableValues(injectableValues)             // 空json 最后居然可以赋值             .readValue("{ }", JacksonInjectUser.class);     Assertions.assertEquals(dynamicValue,jacksonInjectUser.getName()); } 

注意:@JacksonInject中提供了useInput参数进行绑定策略控制。

@JsonAlias

在反序列化的时候来对Java Bean的属性进行名称绑定,可以绑定多个json的键名。举个例子:

    @SneakyThrows     @Test     void jsonAlias(){          // 两个json的类型结构是相同的云服务器提供商 可以定义一个Bean来接收         String userJson = "{ \"name\": \"felord.cn\",\"age\": 22}";         String itemJson = "{ \"category\": \"coco\", \"count\": 50 }";         Domain user = objectMapper.readValue(userJson, Domain.class);         Assertions.assertEquals("felord.cn",user.getStr());         Assertions.assertEquals(22,user.getNum());         Domain item = objectMapper.readValue(itemJson, Domain.class);         Assertions.assertEquals("coco",item.getStr());         Assertions.assertEquals(50,item.getNum());     } @Data public class Domain{      @JsonAlias({ "name","category"})     private String str;     @JsonAlias({ "age","count"})     private Integer num; } 

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

@JsonAnyGetter

在json序列化时可以将Bean中的java.util.Map类型的属性“平铺展开”,举个例子:

某个Java Bean正常的json序列化结果是:

{    "name": "felord.cn",   "age": 22,   "unMatched": {      "unknown": "unknown"   } } 

但是我们需要:

{    "name": "felord.cn",   "age": 22,   "unknown": "unknown" } 

我们可以对Java Bean这么标记:

@Data public class MapUser {      private String name;     private Integer age;     private Map<String,Object> unMatched;     @JsonAnyGetter     public Map<String, Object> getUnMatched() {          return unMatched;     } } 

然后我们来试一试:

@SneakyThrows @Test void jsonAnyGetter(){      MapUser mapUser = new MapUser();     mapUser.setName("felord.cn");     mapUser.setAge(22);     mapUser.setUnMatched(Collections.singletonMap("unknown","unknown"));     String json = objectMapper.writeValueAsString(mapUser);     // 获取json中unknown节点的值     Object read = JsonPath.parse(json)             .read(JsonPath.compile("$.unknown"));     Assertions.assertEquals("unknown",read); } 

不过这个注解的使用也是有条件的:

不能是静态方法。 必须是无参方法。 方法的返回值必须是java.util.Map。 一个实体中只能使用一个该注解。

@JsonAnySetter

正好和@JsonAnyGetter相反,这里就不介绍了。

@JsonAutoDetect

一般情况下,我们认为Jackson序列化对象的前提是有无参构造并且有Getter方法。事实上下面这个类依然可以序列化成json:

@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY) public class ConstructUser {      private final String name;     private final Integer age;     public ConstructUser(String name, Integer age) {          this.name = name;         this.age = age;     } } 

我们可以通过调整Java Bean中属性、getter方法、isGetter方法、setter方法、初始化实例的方法。可见级别可以分为:

DEFAULT: 需要根据上下文来判断,一般基于父类的亿华云可见性。 ANY:任何级别的都可以自动识别。 NONE:所有级别都不可以自动识别。 NON_PRIVATE:非private修饰的可以自动识别。 PROTECTED_AND_PUBLIC:被protected和public修饰的可以被自动识别。 PUBLIC_ONLY:只有被public修饰的才可以被自动识别。

@JsonBackReference

这个注解经常和另一个注解@JsonManagedReference成对出现,它为了解决递归的问题,例如两个类互相持有对方:

Info info = new Info(); Player player = new Player(); player.setId(1); info.setPlayer(player); player.setInfo(info); // 直接无限递归了 String InfiniteRecursionError = objectMapper.writeValueAsString(player); 

json序列化的时候直接无限递归了。如果你想得到下面的序列化结果:

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

就需要在类Player的Info属性上标记@JsonManagedReference,同时在Info类中的Player属性上标记@JsonBackReference注解。

如果你想在序列化Player时直接忽略掉Info属性,即期望得到{ "id":1},只需要在Player的Info属性上标记@JsonBackReference注解。

@JsonClassDescription

Jackson对json schemas的支持,用来生成整个json的描述信息。

@JsonCreator

Jackson在反序列化时默认会去找Java Bean的无参构造,但是有些Bean没有无参构造,这时@JsonCreator就派上用场了。你可以将它标记在构造方法或静态工厂方法上,服务器托管通常它还需要同@JsonProperty或@JacksonInject配合,就像这样:

@Getter public class DescriptionUser {      private final String name;     private final Integer age;     @JsonCreator     public DescriptionUser(@JsonProperty("name") String name,                             @JsonProperty("age") Integer age) {          this.name = name;         this.age = age;     } } 

对应的单元测试:

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

你可以在静态初始化实例工厂方法上试试这个注解。

@JsonEnumDefaultValue

我们在定义性别枚举时往往只定义了男和女两个性别。你不能指望用户守规矩。科学的方法是定义一个枚举用来兜底。就像这样:

public enum Gender {      /**      * Female gender.      */     FEMALE,     /**      * Male gender.      */     MALE,     /**      * Unknown gender.      */     UNKNOWN    } 

当用户乱填的时候都定义为未知。在jackson反序列化支持设置一个默认值来兜底。我们可以在Gender#UNKNOWN上标记@JsonEnumDefaultValue,然后反序列化:

@SneakyThrows @Test void jsonEnumDefaultValue(){      // 开启未知枚举值使用默认值特性     objectMapper.enable(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE);     String maleJson = "{ \"name\": \"felord.cn\",\"age\": 22,\"gender\":\"MALE\"}";     EnumUser male = objectMapper.readValue(maleJson, EnumUser.class);     Assertions.assertEquals(Gender.MALE,male.getGender());     String unknownJson = "{ \"name\": \"felord.cn\",\"age\": 22,\"gender\":\"notClear\"}";     EnumUser unknownGender = objectMapper.readValue(unknownJson, EnumUser.class);     Assertions.assertEquals(Gender.UNKNOWN,unknownGender.getGender()); } 

注意:必须手动jackson开启未知枚举值使用默认值特性。

@JsonFilter

同一个实体类根据不同的场景可能需要不同的序列化策略。比如对于A用户实体的某些字段可见,对于B用户另一些字段可见,实现动态的数据字段权限。这种情况下,jackson中其它一些静态注解就很难实现,借助于@JsonFilter反而简单了,下面是实现方法:

@JsonFilter("role_a") public class OnlyAge extends FilterUser{  } // 不序列化age的策略  @JsonFilter("role_b") public class OnlyNameAndGender extends FilterUser{  } 

接下来定义role_a和role_b的策略:

@SneakyThrows @Test void jsonFilter() {      SimpleFilterProvider simpleFilterProvider = new SimpleFilterProvider();     // role_a只展示age       SimpleBeanPropertyFilter onlyAgeFilter = SimpleBeanPropertyFilter.filterOutAllExcept("age");     // role_b只排除age     SimpleBeanPropertyFilter exceptAgeFilter = SimpleBeanPropertyFilter.serializeAllExcept("age");     simpleFilterProvider.addFilter("role_a", onlyAgeFilter);     simpleFilterProvider.addFilter("role_b", exceptAgeFilter);     objectMapper.setFilterProvider(simpleFilterProvider);     //被JsonFilter标记的类     OnlyAge onlyAgeUser = new OnlyAge();     onlyAgeUser.setName("felord.cn");     onlyAgeUser.setGender(Gender.MALE);     onlyAgeUser.setAge(22);     OnlyNameAndGender onlyNameAndGenderUser = new OnlyNameAndGender();     onlyNameAndGenderUser.setName("felord.cn");     onlyNameAndGenderUser.setGender(Gender.MALE);     onlyNameAndGenderUser.setAge(22);     String onlyAge = objectMapper.writeValueAsString(onlyAgeUser);     // 序列化的json中找不到name节点会抛出PathNotFoundException异常     Assertions.assertThrows(PathNotFoundException.class, () -> JsonPath.parse(onlyAge)             .read(JsonPath.compile("$.name")));     String onlyNameAndGender = objectMapper.writeValueAsString(onlyNameAndGenderUser);     // 序列化的json中找不到age节点会抛出PathNotFoundException异常     Assertions.assertThrows(PathNotFoundException.class, () -> JsonPath.parse(onlyNameAndGender)             .read(JsonPath.compile("$.age"))); } 

思考:结合AOP甚至是Spring Security是不是有搞头?

小结

Jackson是一款非常优秀的json类库,提供了丰富的注解来满足各种场景的需要。本篇介绍了一部分注解的用法和场景。胖哥也根据日常一些场景的需要结合这些注解设计了不少动态的、可扩展的、通用的序列化和反序列化功能,用起来非常方便顺手。只有掌握了技术才能运用技术,后续计划把剩下所有的注解都梳理出来分享给大家。另外keycloak的教程也在准备中,还请多多关注和支持。

本文转载自微信公众号「码农小胖哥」,可以通过以下二维码关注。转载本文请联系码农小胖哥公众号。

很赞哦!(7254)