Hi,大家好,我是抢老婆酸奶的小肥仔。
我们日常开发中,经常会用到json数据格式。在java中使用json也有很多工具包,例如:google的gson,阿里的fastjson,jdk自带的json,及我们今天要说的jackson,各工具包有各自的优点。我们今天就来说说jackson,在我们日常工作中会给我们带来事半功倍的效果。
1、Jackson简介
jackson是一个简单且功能强大的基于java的应用库,它可以便捷的完成Java对象和Json对象的转换。其具有以下特性:
- 高性能且稳定:Jackson低内存占用,对Json和对象的解析都很优秀
- 流行度高:Jackson的社区活跃,有很多程序员在使用,同时Jackson是Spring中默认的JSON/XML解析器
- 便于使用:在Jackso中提供大量的注解,例如我们常用的时间格式化注解@JsonFormat
- 干净的json:创建JSON时干净、紧凑、体积小等特点
2、注解介绍
2.1 序列化与反序列化注解
2.1.1 @JsonAnyGetter与@JsonAnySetter
@JsonAnyGetter
:将一个map集合中的key/value,序列化成为json。
@JsonAnySetter
:反序列化时,将定义以外的元素通过key/value形式保存到map中。
/** * @author: jiangjs * @description: 使用@JsonAnyGetter将map序列化成json * @date: 2023/6/12 11:17 **/ public class MapToJson { public MapToJson(){}; public MapToJson(String name){ this.name = name; }; private String name; private final Map<String,Object> properties = new HashMap<>(); public String getName() { return name; } public void setName(String name) { this.name = name; } @JsonAnyGetter public Map<String,Object> getProperties(){ return properties; } @JsonAnySetter public void setProperties(String key,Object value){ this.properties.put(key,value); } }
复制
测试:
private final static Logger log = LoggerFactory.getLogger(UseJacksonApplicationTests.class); @Test public void mapToJson() throws JsonProcessingException { MapToJson getJson = new MapToJson("张三"); getJson.setProperties("key1","属性1"); getJson.setProperties("key2",2); String jsonStr = new ObjectMapper().writeValueAsString(getJson); log.info("@JsonAnyGetter序列化Map转json:"+jsonStr); String json = "{"name":"张三","s":"属性1","p":2}"; MapToJson setJson = new ObjectMapper().readerFor(MapToJson.class).readValue(json); log.info("@JsonAnySetter反序列化Json转实体:"+setJson.getProperties()); }
复制
输出:
从输出结果可以看出, @JsonAnyGetter
将map中的数据序列化成了json字符串中的一部分内容, @JsonAnySetter
反序列化将json中除了定义的参数外(如:name),其他的都转换成了map。因此我们在调用第三方接口时,如果对方新增或修改了一些参数时,为了达到适配,可以使用这两个注解。
2.1.2 @JsonGetter与@JsonSetter
@JsonGetter
:序列化时,使用get方法时,重新定义字段的名称。例如:定义的get方法:getNickName(),使用@JsonGetter(“nName”)则json字符串中则为nName
@JsonSetter
:反序列化时,将定义接受数据的字段名称的数据,赋值给实体中的字段。例如:定义set方法:setNickName(String nickName),而@JsonSetter(“nName”)则json字符串中的nName的值则赋值给实体中的nickName。
/** * @author: jiangjs * @description: 使用@JsonGetter或@JsonSetter * @date: 2023/6/12 14:12 **/ public class JsonGetAndSet { public JsonGetAndSet(String name,String nickName){ this.name = name; this.nickName = nickName; } private String name; private String nickName; public String getName() { return name; } public void setName(String name) { this.name = name; } @JsonGetter("nName") public String getNickName() { return nickName; } @JsonSetter("nName") public void setNickName(String nickName) { this.nickName = nickName; } }
复制
测试:
@Test public void jsonGetAndSet() throws JsonProcessingException { JsonGetAndSet getJson = new JsonGetAndSet("zhangsan","张三"); String jsonStr = new ObjectMapper().writeValueAsString(getJson); log.info("@JsonGetter序列化实体转json:"+jsonStr); String json = "{"name":"zhangsan","nName":"张三"}"; JsonGetAndSet setJson = new ObjectMapper().readerFor(JsonGetAndSet.class).readValue(json); log.info("@JsonSetter反序列化Json转实体:"+setJson.getName() + "," + setJson.getNickName()); }
复制
输出:
这两个注解可以理解是给字段设置了别名,当我们不想让客户知道具体字段信息或接收的数据中一个字段要获取另一个字段值时,可以采用这两个注解。
2.1.3 @JsonPropertyOrder
@JsonPropertyOrder
:指定实体字段序列化的顺序。
/** * @author: jiangjs * @description: 未使用@JsonPropertyOrder * @date: 2023/6/12 14:35 **/ @Data @AllArgsConstructor public class OrderProperty { private int id; private String name; private String nickName; } /** * @author: jiangjs * @description: 使用@JsonPropertyOrder * @date: 2023/6/12 14:40 **/ @Data @AllArgsConstructor @JsonPropertyOrder({"name","nickName","id"}) public class UseOrderProperty { private int id; private String name; private String nickName; }
复制
测试:
@Test public void usePropertyOrder() throws JsonProcessingException { OrderProperty property = new OrderProperty(1, "zhangsan", "张三"); String json = new ObjectMapper().writeValueAsString(property); log.info("未使用@JsonPropertyOrder排序:"+json); UseOrderProperty propertyOrder = new UseOrderProperty(1, "zhangsan", "张三"); String useJson = new ObjectMapper().writeValueAsString(propertyOrder); log.info("使用@JsonPropertyOrder排序:"+useJson); }
复制
输出:
2.1.4 @JsonRawValue
@JsonRawValue
:按原样序列化属性值。
/** * @author: jiangjs * @description: 使用@JsonRawValue * @date: 2023/6/12 14:55 **/ @Data @AllArgsConstructor public class JsonRawValueDomain { private String name; @JsonRawValue private String json; }
复制
测试:
@Test public void useJsonRawValue() throws JsonProcessingException { JsonRawValueDomain valueDomain = new JsonRawValueDomain("张三", "{"age":30,"gender":"男"}"); String json = new ObjectMapper().writeValueAsString(valueDomain); log.info("使用@JsonRawValue输出json:"+json); }
复制
输出:
- 未使用@JsonRawValue进行注解时,则定义json值未序列化,得到的是赋值的String类型数据
- 使用@JsonRawValue进行注解时,则定义json值得到的是序列化后的数据
2.1.5 @JsonValue
@JsonValue
:序列化时只返回注解字段的值。
/** * @author: jiangjs * @description: * @date: 2023/6/12 15:55 **/ @Data @AllArgsConstructor public class JsonValueDomain { private String name; @JsonValue private String nickName; }
复制
测试:
@Test public void useJsonValue() throws JsonProcessingException { JsonValueDomain valueDomain = new JsonValueDomain("zhangsan", "张三"); String json = new ObjectMapper().writeValueAsString(valueDomain); log.info("未使用@JsonValue输出json:"+json); }
复制
输出:
- 未使用@JsonValue时,返回键值的json
- 使用@JsonValue注解nickName时,则只返回nickName的值。
2.1.6 @JsonRootName
@JsonRootName
:序列化时,给json设置name。
/** * @author: jiangjs * @description: * @date: 2023/6/12 14:35 **/ @Data @AllArgsConstructor @JsonRootName(value = "property") public class OrderProperty { private int id; private String name; private String nickName; }
复制
测试:
@Test public void useJsonRootName() throws JsonProcessingException { OrderProperty property = new OrderProperty(1, "zhangsan", "张三"); String json = new ObjectMapper().writeValueAsString(property); log.info("未使用@JsonRootName:"+json); String json = new ObjectMapper().enable(SerializationFeature.WRAP_ROOT_VALUE).writeValueAsString(property); log.info("使用@JsonRootName:"+json); }
复制
输出:
- 未使用@JsonRootName,则直接返回json的键值对数据
- 使用@JsonRootName后,在json数据前添加了设置的name。
在使用@JsonRootName时,序列化需要指定enable的值,注解才会生效。
2.1.7 @JsonCreator
@JsonCreator
:反序列化时,调用构造方法或工厂,在反序列化时,可以指定json中的字段与实体字段相匹配。例如:json中字段p,则可以用实体中的c与之相匹配。
/** * @author: jiangjs * @description: * @date: 2023/6/13 9:17 **/ @Data public class UseJsonCreator { private String name; private String nickName; @JsonCreator public UseJsonCreator(@JsonProperty("name") String name, @JsonProperty("nName") String nickName){ this.name = name; this.nickName = nickName; } }
复制
测试:
@Test public void useJsonRawValue() throws JsonProcessingException { String json = "{"name":"zhangsan","nName":"张三"}"; UseJsonCreator uc = new ObjectMapper().readerFor(UseJsonCreator.class).readValue(json); log.info("使用@JsonCreator输出实体:"+uc.getName() + uc.getNickName()); }
复制
输出:
2.1.8 @JacksonInject
@JacksonInject
:反序列化时,被注解的参数,只能通过InjectableValues来添加数据,设置表中主键数据时可以使用。
/** * @author: jiangjs * @description: * @date: 2023/6/13 9:32 **/ @Data public class UseJacksonInject { @JacksonInject private String name; private String nickName; }
复制
测试:
@Test public void useJacksonInject() throws JsonProcessingException { String json = "{"nickName":"张三"}"; InjectableValues.Std inject = new InjectableValues.Std().addValue(String.class, "zhangsan"); UseJacksonInject uji = new ObjectMapper().reader(inject).forType(UseJacksonInject.class).readValue(json); log.info("使用@JacksonInject赋值实体:"+uji.getName() + uji.getNickName()); }
复制
输出:
2.1.9 @JsonAlias
@JsonAlias
:给注解字段提供一个或多个别名,反序列化时,根据别名找到对应字段进行赋值。
/** * @author: jiangjs * @description: * @date: 2023/6/13 9:50 **/ @Data public class UseJsonAlias { private String name; @JsonAlias({"nName","pName"}) private String nickName; }
复制
测试:
@Test public void useJsonAlias() throws JsonProcessingException { String json = "{"nName":"张三","name":"zhangsan"}"; UseJsonAlias uji = new ObjectMapper().readerFor(UseJsonAlias.class).readValue(json); log.info("使用@UseJsonAlias别名赋值实体:"+uji.getName() + uji.getNickName()); }
复制
输出:
2.2 属性注解
2.2.1 @JsonIgnoreProperties、@JsonIgnore、 @JsonIgnoreType
@JsonIgnoreProperties
:序列化时设置忽略一个或多个字段,只能使用在类上。
@JsonIgnore
:可以理解是 @JsonIgnoreProperties
忽略一个字段的版本,使用在字段上。
@JsonIgnoreType
:忽略被注解的类的所有属性,即 @JsonIgnoreProperties
忽略该类上的所有字段
/** * @author: jiangjs * @description: * @date: 2023/6/13 9:59 **/ @Data @JsonIgnoreProperties("name") @AllArgsConstructor public class UseJsonIgnoreProperties { private String name; private String nickName; @JsonIgnore private String gender; private IgnoreClass aClass; @Data @JsonIgnoreType @AllArgsConstructor public static class IgnoreClass{ private String loginName; private String passWord; } }
复制
测试:
@Test public void useJsonIgnoreProperties() throws JsonProcessingException { UseJsonIgnoreProperties.IgnoreClass clazz = new UseJsonIgnoreProperties.IgnoreClass("zs","123456"); UseJsonIgnoreProperties ignoreProperties = new UseJsonIgnoreProperties("zhangsan", "张三","男",clazz); String json = new ObjectMapper().writeValueAsString(ignoreProperties); log.info("使用@JsonIgnoreProperties与@JsonIgnore忽略设置字段:"+json); }
复制
输出:
2.2.2 @JsonInclude与@JsonIncludeProperties
@JsonInclude
:序列化时,获取符合条件的字段。例如:设置序列化不为null的字段
@JsonIncludeProperties
:用于设置序列化或反序列化时,指定的一个或多个字段。版本2.12及以上才支持。
/** * @author: jiangjs * @description: * @date: 2023/6/13 10:31 **/ @Data @AllArgsConstructor @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIncludeProperties({"nickName","name"}) public class UseJsonInclude { private String name; private String nickName; private String gender; }
复制
只获取不为null且序列化nickName,name的字段。
测试:
@Test public void useJsonInclude() throws JsonProcessingException { UseJsonInclude include = new UseJsonInclude("zhangsan", null, "男"); String json = new ObjectMapper().writeValueAsString(include); log.info("使用@JsonInclude与@JsonIncludeProperties序列化:"+json); }
复制
输出:
2.2.3 @JsonAutoDetect
@JsonAutoDetect
:可以设置哪些属性可见或不可见。默认情况下,Jackson 只使用 public 的字段进行序列化和反序列化。没有 public 字段时,会使用 public 的 getters/setters。
可以通过 @JsonAutoDetect 自定义这种行为,指定字段、方法的可见性规则。
/** * @author: jiangjs * @description: * @date: 2023/6/13 11:12 **/ @AllArgsConstructor @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.PROTECTED_AND_PUBLIC) public class UseJsonAutoDetect { private final String name; private final String nickName; public String gender; protected Integer age; }
复制
设置了PROTECTED_AND_PUBLIC可见
测试:
@Test public void useJsonAutoDetect() throws JsonProcessingException { UseJsonAutoDetect autoDetect = new UseJsonAutoDetect("zhangsan", "张三","男",30); String json = new ObjectMapper().writeValueAsString(autoDetect); log.info("使用@JsonAutoDetect序列化:"+json); }
复制
输出:
JsonAutoDetect.Visibility.PROTECTED_AND_PUBLIC
:字段protected和public可见。
JsonAutoDetect.Visibility.ANY
:所有可见
JsonAutoDetect.Visibility.NON_PRIVATE
:除private外可见
JsonAutoDetect.Visibility.PUBLIC_ONLY
:仅public可见
JsonAutoDetect.Visibility.NONE
:禁用JsonAutoDetect
2.3 多态注解
@JsonTypeInfo
:作用于类/接口,说明开启了多态类型处理。
@JsonSubType
:用来列举子类,当子类类型无法被检测时使用它,与@JsonTypeInfo配合使用
@JsonTypeName
:作用于子类,即给每个子类指定名称。
/** * @author: jiangjs * @description: @JsonTypeInfo实现多态 * @date: 2023/6/13 14:12 **/ @JsonTypeInfo(use = JsonTypeInfo.Id.NAME,include = JsonTypeInfo.As.PROPERTY,property="type") @JsonSubTypes({ @JsonSubTypes.Type(value = Dog.class,name = "dog"), @JsonSubTypes.Type(value = Cat.class,name = "cat"), }) @Data @Accessors(chain = true) public class Animal { private String name; private String color; }
复制
/** * @author: jiangjs * @description: * @date: 2023/6/13 14:14 **/ @EqualsAndHashCode(callSuper = true) @JsonTypeName(value = "cat") @Data public class Cat extends Animal{ boolean likesCream; public int lives; } /** * @author: jiangjs * @description: * @date: 2023/6/13 14:16 **/ @EqualsAndHashCode(callSuper = true) @Data @JsonTypeName(value = "dog") @Accessors(chain = true) public class Dog extends Animal{ public double barkVolume; }
复制
测试:
@Test public void useJsonTypeInfo() throws JsonProcessingException { Dog dog = new Dog(); dog.setBarkVolume(10d).setColor("黄白").setName("橙橙"); String json = new ObjectMapper().writeValueAsString(dog); log.info("使用@JsonTypeInfo多态:"+json); String cat = "{"name":"橙橙","color":"黄白","barkVolume":10.0,"lives":1,"likesCream":true}"; Animal animal = new ObjectMapper().readerFor(Animal.class).readValue(cat); log.info("使用@JsonTypeInfo多态:"+animal.getName()); }
复制
当实体被序列化时,会在json中添加了type这个字段,用于区分具体的子类。反序列化时,如果没有添加这个type字段时,则会报错。而type这个字段是在 @JsonTypeInfo
下的property定义
输出:
在@JsonTypeInfo中一些属性
1、use:
JsonTypeInfo.Id.CLASS
:使用完整的类名作为属性标识,例如:上述中dog被序列化后看到其完整的类路径。 “type”:“com.jiashn.useJackson.domain.Dog” ;
JsonTypeInfo.Id.MINIMAL_CLASS
:若基类与子类在同一包类,则使用类名来作为属性标识。例如:dog被序列化后: “type”:“.Dog” ;
JsonTypeInfo.Id.NAME
:与@JsonTypeName一起使用,指定设置的类型名称。例如:Dog子类被设置成dog,序列化后: “type”:“dog” ;
JsonTypeInfo.Id.CUSTOM
:自定义识别码,由 @JsonTypeIdResolver 设置
JsonTypeInfo.Id.NONE
:不适用标识。2、include:可选,指定标识以怎样的方式进行序列化。
JsonTypeInfo.As.PROPERTY
:作为数据的兄弟属性,即与数据在同一级。
JsonTypeInfo.As.EXISTING_PROPERTY
:作为实体中已存在的属性
JsonTypeInfo.As.EXTERNAL_PROPERTY
:作为扩展属性
JsonTypeInfo.As.WRAPPER_OBJECT
:作为一个包装的对象
JsonTypeInfo.As.WRAPPER_ARRAY
:作为一个包装的数组3、property:可选,设置属性标记
2.4 常规注解
2.4.1 @JsonProperty
@ JsonProperty
:指定字段在json显示的属性。
/** * @author: jiangjs * @description: * @date: 2023/6/13 15:06 **/ @Data @AllArgsConstructor public class UseJsonProperty { @JsonProperty("loginName") private String name; private String nickName; }
复制
测试:
@Test public void useJsonProperty() throws JsonProcessingException { UseJsonProperty jsonProperty = new UseJsonProperty("zhangsan", "张三"); String json = new ObjectMapper().writeValueAsString(jsonProperty); log.info("使用@JsonProperty指定属性:" + json); }
复制
输出:
2.4.2 @JsonFormat
@JsonFormat
:序列化时格式化时间,这个也是我们经常用到的一个注解。
/** * @author: jiangjs * @description: * @date: 2023/6/13 15:06 **/ @Data @AllArgsConstructor public class UseJsonProperty { @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd kk:mm:ss") private Date date; }
复制
测试:
@Test public void useJsonProperty() throws JsonProcessingException { UseJsonProperty jsonProperty = new UseJsonProperty("new Date()); String json = new ObjectMapper().writeValueAsString(jsonProperty); log.info("使用@JsonFormat指定属性:" + json); }
复制
输出:
pattern
:格式化格式,如:yyyy-MM-dd HH:mm:ss
timezone
:时区,例如:GMT+8
locale
:根据位置序列化后的一种语言格式,例如:zh_CN表示中国
shap
:序列化后的类型,默认:JsonFormat.Shape.ANY
2.4.3 @JsonUnwrapped
@ JsonUnwrapped
:将被注解的实体类数据,与其他字段同一级在json中显示。
/** * @author: jiangjs * @description: * @date: 2023/6/14 10:07 **/ @Data @AllArgsConstructor public class UseJsonUnwrapped { private String name; @JsonUnwrapped private Other other; @Data @AllArgsConstructor public static class Other{ private String nickName; private String gender; } }
复制
测试:
@Test public void useJsonUnwrapped() throws JsonProcessingException { UseJsonUnwrapped.Other other = new UseJsonUnwrapped.Other("张三", "男"); UseJsonUnwrapped unwrapped = new UseJsonUnwrapped("zhangsan", other); String json = new ObjectMapper().writeValueAsString(unwrapped); log.info("使用@JsonFormat指定属性:" + json); }
复制
输出:
- Other属性未添加@JsonUnwrapped时
- Other属性添加@JsonUnwrapped时
2.4.4 @JsonView
@JsonView
:标识在View中的哪些属性进行序列化或反序列化。
/** * @author: jiangjs * @description: * @date: 2023/6/14 10:18 **/ public class Views { public static class Public {} public static class Internal extends Public {} } /** * @author: jiangjs * @description: * @date: 2023/6/14 10:15 **/ @Data @AllArgsConstructor public class UseJsonView { @JsonView(Views.Public.class) private String name; @JsonView(Views.Public.class) private String nickName; @JsonView(Views.Internal.class) private String gender; }
复制
测试:
@Test public void useJsonView() throws JsonProcessingException { UseJsonView view = new UseJsonView("zhangsan","张三", "男"); String json = new ObjectMapper().writerWithView(Views.Public.class).writeValueAsString(view); log.info("使用@JsonView序列化json:" + json); }
复制
输出:
writerWithView()中指定了只显示Views中哪些属性的值。
2.4.5 @JsonManagedReference, @JsonBackReference
@JsonManagedReference, @JsonBackReference
:一起处理父类与子类之间的关系,解决循环。
/** * @author: jiangjs * @description: * @date: 2023/6/14 10:39 **/ @Data @AllArgsConstructor public class Parent { private Integer id; private String name; @JsonManagedReference Child children; } /** * @author: jiangjs * @description: * @date: 2023/6/14 10:40 **/ @Data public class Child { private String childName; @JsonBackReference List<Parent> parent; public Child(){} public Child(String childName){this.childName = childName;} }
复制
测试:
@Test public void useReference() throws JsonProcessingException { Child child = new Child("孩子"); Parent parent = new Parent(1, "父辈", child); String json = new ObjectMapper().writeValueAsString(parent); log.info("使用@JsonManagedReference, @JsonBackReference序列化json:" + json); }
复制
输出:
- 未添加注解时
子类中的parent没有赋值,则显示的是null,具有依赖关系
- 添加注解后
子类中没有parent,解决了依赖。
2.4.6 @JsonIdentityInfo
@JsonIdentityInfo
:表示在序列化/反序列化值时应使用对象标识,可以解决循环依赖问题。
/** * @author: jiangjs * @description: * @date: 2023/6/14 10:39 **/ @Data @Accessors(chain = true) @JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class,property = "id") public class IdentityParent { private Integer id; private String name; private List<IdentityChild> children; } /** * @author: jiangjs * @description: * @date: 2023/6/14 10:40 **/ @Data @Accessors(chain = true) public class IdentityChild { private int id; private String childName; private IdentityParent identityParent; }
复制
测试:
@Test public void useJsonIdentityInfo() throws JsonProcessingException { IdentityChild identityChild = new IdentityChild(); identityChild.setId(2).setChildName("孩子"); IdentityParent parent = new IdentityParent(); parent.setName("父辈").setId(1).setChildren(Collections.singletonList(identityChild)); identityChild.setIdentityParent(parent); String json = new ObjectMapper().writeValueAsString(parent); log.info("使用@JsonIdentityInfo序列化json:" + json); }
复制
输出:
identityChild将IdentityParent初始化的数据赋值给了字段identityParent,但是输出的结果是IdentityParent中的id的值,即指向的是IdentityParent中设置的标识。
2.4.7 @JsonFilter
@JsonFilter:序列化时,指定设置过滤器,设置要输出的字段。
/** * @author: jiangjs * @description: * @date: 2023/6/14 17:23 **/ @Data @JsonFilter("jsonFilter") @AllArgsConstructor public class UseJsonFilter { private String name; private String nickname; }
复制
测试:
@Test public void useJsonFilter() throws JsonProcessingException { UseJsonFilter filter = new UseJsonFilter("zhangsan","张三"); SimpleFilterProvider provider = new SimpleFilterProvider() .addFilter("jsonFilter", SimpleBeanPropertyFilter.filterOutAllExcept("nickname")); String json = new ObjectMapper().writer(provider).writeValueAsString(filter); log.info("使用@JsonFilter序列化json:" + json); }
复制
输出:
jackson的注解就先跟大家说到这,这些注解中有很多我们可以在日常开发中用到,比如:@JsonAnyGetter和@JsonAnySetter,再确定主要接口字段后,其他不确定字段就可以使用。这些接口要熟练掌握,需要我们在日常开发中常常用到,希望我的文章对大家有所帮助,工欲善其事,必先利其器,撸起来吧,小伙伴们。
下次我们来说说jackson的自定义注解以及相关应用。