认识Jackson

Jackson 是当前用的比较广泛的,用来序列化和反序列化 json 的 Java 的开源框架。Jackson 社 区相对比较活跃,更新速度也比较快, 从 Github 中的统计来看,Jackson 是最流行的 json 解析器之一 。 Spring MVC 的默认 json 解析器便是 Jackson。 Jackson 优点很多。 Jackson 所依赖的 jar 包较少 ,简单易用。与其他 Java 的 json 的框架 Gson 等相比, Jackson 解析大的 json 文件速度比较快;Jackson 运行时占用内存比较低,性能比较好;Jackson 有灵活的 API,可以很容易进行扩展和定制。

Jackson 的 1.x 版本的包名是 org.codehaus.jackson ,当升级到 2.x 版本时,包名变为 com.fasterxml.jackson,本文讨论的内容是基于最新的 Jackson 的 2.9.1 版本。

Jackson 的核心模块由三部分组成。

  • jackson-core,核心包,提供基于 “流模式” 解析的相关 API,它包括 JsonPaser 和 JsonGenerator。 Jackson 内部实现正是通过高性能的流模式 API 的 JsonGenerator 和 JsonParser 来生成和解析 json。
  • jackson-annotations,注解包,提供标准注解功能;
  • jackson-databind ,数据绑定包, 提供基于 “对象绑定” 解析的相关 API ( ObjectMapper ) 和 “树模型” 解析的相关 API (JsonNode);基于 “对象绑定” 解析的 API 和 “树模型” 解析的 API 依赖基于 “流模式” 解析的 API。

清单 1. 在 pom.xml 的 Jackson 的配置信息
  1. <dependency>
  2. <groupId>com.fasterxml.jackson.core</groupId>
  3. <artifactId>jackson-databind</artifactId>
  4. <version>2.9.1</version>
  5. </dependency>

jackson-databind 依赖 jackson-core 和 jackson-annotations,当添加 jackson-databind 之后, jackson-core 和 jackson-annotations 也随之添加到 Java 项目工程中。在添加相关依赖包之后,就可以使用 Jackson。

ObjectMapper 的 使用

Jackson 最常用的 API 就是基于 “对象绑定” 的 ObjectMapper。下面是一个 ObjectMapper 的使用的简单示例。

清单 2 . ObjectMapper 使用示例
  1. ObjectMapper mapper = new ObjectMapper();
  2. Person person = new Person();
  3. person.setName("Tom");
  4. person.setAge(40);
  5. String jsonString = mapper.writerWithDefaultPrettyPrinter()
  6. .writeValueAsString(person);
  7. Person deserializedPerson = mapper.readValue(jsonString, Person.class);

ObjectMapper 通过 writeValue 系列方法 将 java 对 象序列化 为 json,并 将 json 存 储成不同的格式,String(writeValueAsString),Byte Array(writeValueAsString),Writer, File,OutStream 和 DataOutput。

ObjectMapper 通过 readValue 系列方法从不同的数据源像 String , Byte Array, Reader,File,URL, InputStream 将 json 反序列化为 java 对象。

信息配置

在调用 writeValue 或调用 readValue 方法之前,往往需要设置 ObjectMapper 的相关配置信息。这些配置信息应用 java 对象的所有属性上。示例如下:

清单 3 . 配置信息使用示例
  1. //在反序列化时忽略在 json 中存在但 Java 对象不存在的属性
  2. mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,
  3. false);
  4. //在序列化时日期格式默认为 yyyy-MM-dd'T'HH:mm:ss.SSSZ
  5. mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS,false)
  6. //在序列化时忽略值为 null 的属性
  7. mapper.setSerializationInclusion(Include.NON_NULL);
  8. //忽略值为默认值的属性
  9. mapper.setDefaultPropertyInclusion(Include.NON_DEFAULT);

更多配置信息可以查看 Jackson 的 DeserializationFeature,SerializationFeature 和 Include。

Jackson 的 注解的使用

Jackson 根据它的默认方式序列化和反序列化 java 对象,若根据实际需要,灵活的调整它的默认方式,可以使用 Jackson 的注解。

使用ObjectMapper

了解ObjectMapper对象

  1. private final static ObjectMapper MAPPER = new ObjectMapper();

单个对象序列化

对象转字符串

  1. String studentStr = MAPPER.writeValueAsString(student);

对象转byte数组

  1. byte[] bytes = MAPPER.writeValueAsBytes(student);

对象转JSON文件

  1. MAPPER.writeValue(new File("D:/test/student.json"), student);

单个对象反序列化

字符串转对象

  1. Student student = MAPPER.readValue(jsonStr, Student.class);

byte数组转对象

  1. Student tFromBytes = MAPPER.readValue(bytes, Student.class);

JSON文件转对象

  1. Student tFromBytes = MAPPER.readValue(new File("E:/test/student.json"), Student.class);

Map序列化

HashMap转字符串

  1. HashMap<String,Object> map = new HashMap();
  2. map.put("id","001");
  3. map.put("name","李白");
  4. map.put("age",320);
  5. map.put("isBoy",true);
  6. String mapStr = MAPPER.writeValueAsString(map);

Map反序列化

将字符串转Map对象

  1. String str = "{\"name\":\"李白\",\"id\":\"001\",\"age\":320,\"isBoy\":true}";
  2. try {
  3. HashMap<String, Object> map = MAPPER.readValue(str, new TypeReference<HashMap<String, Object>>() {});
  4. System.out.println(map);
  5. } catch (JsonProcessingException e) {
  6. e.printStackTrace();
  7. }

JSON数组的反序列化

从 JSON 数组字符串中读取对象数组

  1. private static void strToArray(){
  2. String str = "[{\"id\":\"001\",\"name\":\"李白\",\"age\":322,\"gender\":1},{\"id\":\"002\",\"name\":\"武则天\",\"age\":330,\"gender\":0},{\"id\":\"003\",\"name\":\"张飞\",\"age\":310,\"gender\":1},{\"id\":\"004\",\"name\":\"赵云\",\"age\":410,\"gender\":1}]";
  3. try {
  4. Student[] students = MAPPER.readValue(str, Student[].class);
  5. Arrays.stream(students).forEach(System.out::println);
  6. } catch (JsonProcessingException e) {
  7. e.printStackTrace();
  8. }
  9. }

从 JSON 数组字符串中读取对象列表

  1. private static void strToList(){
  2. String str = "[{\"id\":\"001\",\"name\":\"李白\",\"age\":322,\"gender\":1},{\"id\":\"002\",\"name\":\"武则天\",\"age\":330,\"gender\":0},{\"id\":\"003\",\"name\":\"张飞\",\"age\":310,\"gender\":1},{\"id\":\"004\",\"name\":\"赵云\",\"age\":410,\"gender\":1}]";
  3. try {
  4. List<Student> students = MAPPER.readValue(str, new TypeReference<List<Student>>() {});
  5. students.forEach(System.out::println);
  6. } catch (JsonProcessingException e) {
  7. e.printStackTrace();
  8. }
  9. }

树模型JsonNode

ObjectMapper 对象有一个特殊的方法 readTree(),它总是返回一个JsonNode 对象。
image.png
通过 JsonNode 可以直接读取JSON节点值:

  1. JsonNode jsonNode = mapper.readTree(mapJsonStr);
  2. String name = jsonNode.get("name").asText();
  3. int age = jsonNode.get("age").asInt();
  4. String city = jsonNode.get("addr").get("city").asText();
  5. String street = jsonNode.get("addr").get("street").asText();

对象转JsonNode

  1. Car car = new Car();
  2. car.brand = "Cadillac";
  3. car.doors = 4;
  4. JsonNode carJsonNode = MAPPER.valueToTree(car);

JsonNode 转对象

  1. String carJson = "{ \"brand\" : \"Mercedes\", \"doors\" : 5 }";
  2. JsonNode carJsonNode = MAPPER.readTree(carJson);
  3. Car car = objectMapper.treeToValue(carJsonNode,Car.class);

常用配置

  1. ObjectMapper mapper = new ObjectMapper();
  2. //序列化结果格式化
  3. mapper.enable(SerializationFeature.INDENT_OUTPUT);
  4. // Date、Calendar等序列化为时间格式的字符串(如果不执行以下设置,就会序列化成时间戳格式);
  5. mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
  6. //在序列化时日期格式默认为 yyyy-MM-dd'T'HH:mm:ss.SSSZ
  7. mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS,false);
  8. //空对象不要抛出异常
  9. mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
  10. // 在反序列化时忽略在 json 中存在但 Java 对象不存在的属性,遇到未知属性不要抛出异常:
  11. mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
  12. // 反序列化时,空字符串对于的实例属性为null
  13. mapper.enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT);
  14. //忽略值为默认值的属性
  15. mapper.setDefaultPropertyInclusion(Include.NON_DEFAULT);
  16. //允许C和C++样式注释
  17. mapper.configure(JsonParser.Feature.ALLOW_COMMENTS, true);
  18. //允许字段名没有引号(可以进一步减小json体积)
  19. mapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
  20. //允许单引号
  21. mapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);

特殊配置:在json对象最外层再包裹一层

先来看看正常情况一个普通的序列化结果:

  1. {
  2. "id" : 1,
  3. "text" : "aabbcc",
  4. "fromUserId" : 456,
  5. "toUserId" : 0,
  6. "languageCode" : "zh"
  7. }

接下来咱们做两件事,首先,是给上述json对应的实例类添加一个注解,如下图红框:
image.png
其次,执行以下配置:

  1. mapper.enable(SerializationFeature.WRAP_ROOT_VALUE);

然后再次执行序列化,得到的结果如下,可见和之前的序列化结果相比,之前的整个json都变成了一个value,此value对应的key就是注解JsonRootName的value属性:

  1. {
  2. "aaa" : {
  3. "id" : 1,
  4. "text" : "aabbcc",
  5. "fromUserId" : 456,
  6. "toUserId" : 0,
  7. "languageCode" : "zh"
  8. }
  9. }

使用 ObjectMapper 读取和编写 Yaml

要引入依赖包:

  1. <dependency>
  2. <groupId>com.fasterxml.jackson.dataformat</groupId>
  3. <artifactId>jackson-dataformat-yaml</artifactId>
  4. <version>2.11.0</version>
  5. </dependency>

对象转Yaml格式字符串

  1. /**
  2. * 对象转Yaml字符串
  3. */
  4. private static void objectToYamlStr(){
  5. Employee employee = new Employee("张山", 22, "1008611");
  6. // 将对象转Yaml字符串
  7. ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
  8. // 格式化输出
  9. mapper.enable(SerializationFeature.INDENT_OUTPUT);
  10. try {
  11. String yamlEmployeeStr = mapper.writeValueAsString(employee);
  12. System.out.println(yamlEmployeeStr);
  13. } catch (JsonProcessingException e) {
  14. e.printStackTrace();
  15. }
  16. }

yamlEmployeeStr变量包含Employee在执行此代码后序列化为 YAML 数据格式的对象。
执行后打印效果:

  1. name: "张山"
  2. age: 22
  3. phone: "1008611"

Yaml字符串转对象

  1. /**
  2. * Yaml字符串转对象
  3. */
  4. private static void yamlStrToObject(){
  5. String str = "name: \"张山\"\n" +
  6. "age: 22\n" +
  7. "phone: \"1008611\"";
  8. ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
  9. try {
  10. Employee employee = mapper.readValue(str, Employee.class);
  11. System.out.println(employee);
  12. } catch (JsonProcessingException e) {
  13. e.printStackTrace();
  14. }
  15. }

对象转YAML文件

  1. /**
  2. * 对象转Yaml文件
  3. */
  4. private static void objectToYamlFile(){
  5. Employee employee = new Employee("张山", 22, "1008611");
  6. ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
  7. try {
  8. mapper.writeValue(new File("E:/test/emplay.yaml"),employee);
  9. } catch (IOException e) {
  10. e.printStackTrace();
  11. }
  12. }

Yaml文件转对象

  1. /**
  2. * 读取Yaml文件转Employee对象
  3. */
  4. private static void yamlFileToObject(){
  5. ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
  6. try {
  7. Employee employee = mapper.readValue(new File("E:/test/emplay.yaml"), Employee.class);
  8. System.out.println(employee);
  9. } catch (IOException e) {
  10. e.printStackTrace();
  11. }
  12. }

参考

https://blog.csdn.net/boling_cavalry/article/details/108192174
https://www.cnblogs.com/guanbin-529/p/11488869.html#_label2

WRAP_ROOT_VALUE

关于root对象(WRAP_ROOT_VALUE)

  1. 对于只有id和name两个字段的POJO实例来说,正常的序列化结果如下:

    1. {
    2. "id":"001",
    3. "name":"张三"
    4. }
  2. jackson在序列化时,可以在上述json外面再包裹一层,官方叫做WRAP_ROOT_VALUE,本文中叫做root对象,如下所示,整个json的只有一个键值对,key是aaabbbccc,value内部才是POJO实例的id和name字段的值: ```json { “aaabbbccc” : { “id” : 2, “name” : “food” } }

  1. <a name="HDRhj"></a>
  2. ## 使用 WRAP_ROOT_VALUE
  3. <a name="o3BSJ"></a>
  4. ### 序列化场景
  5. 1. 配置mapper,开启root对象,序列化的的对象是在json对象最外层再包裹一层:
  6. ```java
  7. mapper.enable(SerializationFeature.WRAP_ROOT_VALUE);

反序列化场景

配置mapper,jackson在反序列化时会先解析root对象:

  1. mapper.enable(DeserializationFeature.UNWRAP_ROOT_VALUE);

代码验证

准备两个POJO

不带有 @JsonRootName 注解,Person1:

  1. public class Person1 {
  2. private String name;
  3. private Integer age;
  4. public Person1() {
  5. }
  6. public Person1(String name, Integer age) {
  7. this.name = name;
  8. this.age = age;
  9. }
  10. public String getName() {
  11. return name;
  12. }
  13. public void setName(String name) {
  14. this.name = name;
  15. }
  16. public Integer getAge() {
  17. return age;
  18. }
  19. public void setAge(Integer age) {
  20. this.age = age;
  21. }
  22. @Override
  23. public String toString() {
  24. return "Person1{" +
  25. "name='" + name + '\'' +
  26. ", age=" + age +
  27. '}';
  28. }
  29. }

带有@JsonRootName 注解,Person2:

  1. @JsonRootName("ppp")
  2. public class Person2 {
  3. private String name;
  4. private Integer age;
  5. public Person2() {
  6. }
  7. public Person2(String name, Integer age) {
  8. this.name = name;
  9. this.age = age;
  10. }
  11. public String getName() {
  12. return name;
  13. }
  14. public void setName(String name) {
  15. this.name = name;
  16. }
  17. public Integer getAge() {
  18. return age;
  19. }
  20. public void setAge(Integer age) {
  21. this.age = age;
  22. }
  23. @Override
  24. public String toString() {
  25. return "Person2{" +
  26. "name='" + name + '\'' +
  27. ", age=" + age +
  28. '}';
  29. }
  30. }

测试序列化

  1. public static void main(String[] args) throws JsonProcessingException {
  2. ObjectMapper mapper = new ObjectMapper();
  3. // 用来美化输出
  4. mapper.enable(SerializationFeature.INDENT_OUTPUT);
  5. Person1 person1 = new Person1("张三", 22);
  6. Person2 person2 = new Person2("李四", 21);
  7. String person1Str = mapper.writeValueAsString(person1);
  8. System.out.println("不设置Root时,正常序列化Person1:\n"+person1Str);
  9. String person2Str = mapper.writeValueAsString(person2);
  10. System.out.println("不设置Root时,正常序列化Person2:\n"+person2Str);
  11. // 开启Root
  12. mapper.enable(SerializationFeature.WRAP_ROOT_VALUE);
  13. String person1RootStr = mapper.writeValueAsString(person1);
  14. System.out.println("设置Root时,序列化Person1:\n"+person1RootStr);
  15. String person2RootStr = mapper.writeValueAsString(person2);
  16. System.out.println("设置Root时,序列化Person2:\n"+person2RootStr);
  17. }

输出:

  1. 不设置Root时,正常序列化Person1
  2. {
  3. "name" : "张三",
  4. "age" : 22
  5. }
  6. 不设置Root时,正常序列化Person2
  7. {
  8. "name" : "李四",
  9. "age" : 21
  10. }
  11. 设置Root时,序列化Person1
  12. {
  13. "Person1" : {
  14. "name" : "张三",
  15. "age" : 22
  16. }
  17. }
  18. 设置Root时,序列化Person2
  19. {
  20. "ppp" : {
  21. "name" : "李四",
  22. "age" : 21
  23. }
  24. }
  25. Process finished with exit code 0

开启 WRAP_ROOT_VALUE 后,最外层会多出一层key,如果有 @JsonRootName 注解,key的名称就为注解的value,如“ppp”,如果没有注解,默认是类名,这里是”Person1”。

测试反序列化

  1. 默认情况下,反序列化时json字符串不能有root对象:

    1. ObjectMapper mapper = new ObjectMapper();
    2. String strWithoutRoot = "{\n" +
    3. " \"name\" : \"李四\",\n" +
    4. " \"age\" : 21\n" +
    5. " }";
    6. String strWithRoot = "{\n" +
    7. " \"ppp\" : {\n" +
    8. " \"name\" : \"李四\",\n" +
    9. " \"age\" : 21\n" +
    10. " }\n" +
    11. "}";
    12. Person2 person21 = mapper.readValue(strWithoutRoot, Person2.class);
    13. System.out.println(person21);
    14. Person2 person22 = mapper.readValue(strWithRoot, Person2.class);
    15. System.out.println(person22);

    输出:

    1. Person2{name='李四', age=21}
    2. com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "ppp" (class com.hlkui.example.jackson.demo.wraprootvalue.Person2), not marked as ignorable (2 known properties: "name", "age"])
    3. at [Source: (String)"{
    4. "ppp" : {
    5. "name" : "李四",
    6. "age" : 21
    7. }
    8. }";......

    反序列化时jackson并不认识ppp这个key,因为jackson此时并不支持root对象。
    小结:默认情况下,反序列化时json字符串不能有root对象;

  2. 如果开启了UNWRAP_ROOT_VALUE属性,用于反序列化的json字符串就必须要有root对象了,开启UNWRAP_ROOT_VALUE属性的代码如下: ```java mapper.enable(DeserializationFeature.UNWRAP_ROOT_VALUE);

  1. 修改代码:
  2. ```java
  3. private static void testDeSerialize2() throws JsonProcessingException {
  4. ObjectMapper mapper = new ObjectMapper();
  5. mapper.enable(DeserializationFeature.UNWRAP_ROOT_VALUE);
  6. String strWithRoot = "{\n" +
  7. " \"ppp\" : {\n" +
  8. " \"name\" : \"李四\",\n" +
  9. " \"age\" : 21\n" +
  10. " }\n" +
  11. "}";
  12. Person2 person22 = mapper.readValue(strWithRoot, Person2.class);
  13. System.out.println(person22);
  14. }
  1. Person2{name='李四', age=21}

可见带有root对象的json字符串,可以反序列化成功,root对象的key就是JsonRootName注解的value属性。

值得注意的是,上述json字符串中,root对象的key为ppp,这和Person2的JsonRootName注解的value值是一致的,如果不一致就会反序列化失败。

注解的使用

JsonInclude 注解

JsonInclede 注解仅在序列化操作时有用,用于控制方法、属性等是否应该被序列化;

JsonInclude 注解的值有多种,每种都有不同效果:

  1. ALWAYS // 默认策略,任何情况都执行序列化
  2. NON_NULL // 非null
  3. NON_ABSENT // null的不会序列化,但如果类型是AtomicReference,依然会被序列化
  4. NON_EMPTY // null、集合数组等没有内容、空字符串等,都不会被序列化
  5. NON_DEFAULT // 如果字段是默认值,就不会被序列化
  6. CUSTOM // 此时要指定valueFilter属性,该属性对应一个类,用来自定义判断被JsonInclude修饰的字段是否序列化
  7. USE_DEFAULTS // 当JsonInclude在类和属性上都有时,优先使用属性上的注解,此时如果在序列化的get方法上使用了JsonInclude,并设置为USE_DEFAULTS,就会使用类注解的设置

ALWAYS

ALWAYS表示全部序列化,是默认策略,null和空字符串都会序列化:

  1. package com.hlkui.example.jackson.demo.annotation;
  2. import com.fasterxml.jackson.annotation.JsonInclude;
  3. import com.fasterxml.jackson.core.JsonProcessingException;
  4. import com.fasterxml.jackson.databind.ObjectMapper;
  5. public class JsonIncludeAlwaysTest {
  6. @JsonInclude(JsonInclude.Include.ALWAYS)
  7. static class Product {
  8. private String name;
  9. private Integer price;
  10. private String color;
  11. public String getName() {
  12. return name;
  13. }
  14. public void setName(String name) {
  15. this.name = name;
  16. }
  17. public Integer getPrice() {
  18. return price;
  19. }
  20. public void setPrice(Integer price) {
  21. this.price = price;
  22. }
  23. public String getColor() {
  24. return color;
  25. }
  26. public void setColor(String color) {
  27. this.color = color;
  28. }
  29. @Override
  30. public String toString() {
  31. return "Product{" +
  32. "name='" + name + '\'' +
  33. ", price=" + price +
  34. ", color='" + color + '\'' +
  35. '}';
  36. }
  37. }
  38. public static void main(String[] args) throws JsonProcessingException {
  39. ObjectMapper mapper = new ObjectMapper();
  40. Product product = new Product();
  41. product.setName("牙膏");
  42. product.setColor("");
  43. System.out.println(product);
  44. String s = mapper.writeValueAsString(product);
  45. System.out.println(s);
  46. }
  47. }
  1. Product{name='牙膏', price=null, color=''}
  2. {"name":"牙膏","price":null,"color":""}

NON_NULL

值为null就不序列化:

  1. package com.hlkui.example.jackson.demo.annotation;
  2. import com.fasterxml.jackson.annotation.JsonInclude;
  3. import com.fasterxml.jackson.core.JsonProcessingException;
  4. import com.fasterxml.jackson.databind.ObjectMapper;
  5. public class JsonIncludeNotNullTest {
  6. @JsonInclude(JsonInclude.Include.NON_NULL)
  7. static class MyOb{
  8. private String name;
  9. private String title;
  10. private Integer age;
  11. public String getName() {
  12. return name;
  13. }
  14. public void setName(String name) {
  15. this.name = name;
  16. }
  17. public String getTitle() {
  18. return title;
  19. }
  20. public void setTitle(String title) {
  21. this.title = title;
  22. }
  23. public Integer getAge() {
  24. return age;
  25. }
  26. public void setAge(Integer age) {
  27. this.age = age;
  28. }
  29. @Override
  30. public String toString() {
  31. return "MyOb{" +
  32. "name='" + name + '\'' +
  33. ", title='" + title + '\'' +
  34. ", age=" + age +
  35. '}';
  36. }
  37. }
  38. public static void main(String[] args) throws JsonProcessingException {
  39. ObjectMapper mapper = new ObjectMapper();
  40. MyOb ob = new MyOb();
  41. ob.setName("111");
  42. ob.setTitle(null);
  43. ob.setAge(13);
  44. System.out.println(ob);
  45. String s = mapper.writeValueAsString(ob);
  46. System.out.println(s);
  47. }
  48. }
  1. MyOb{name='111', title='null', age=13}
  2. {"name":"111","age":13}

title 属性为空,不进行序列化。

NON_ABSENT

设置成NON_NULL时jackson对Optional和AtomicReference的处理,下面的代码中,Optional和AtomicReference的引用都是空,但还是被序列化出来了:

  1. package com.hlkui.example.jackson.demo.annotation;
  2. import com.fasterxml.jackson.annotation.JsonInclude;
  3. import com.fasterxml.jackson.core.JsonProcessingException;
  4. import com.fasterxml.jackson.databind.ObjectMapper;
  5. import java.util.Optional;
  6. import java.util.concurrent.atomic.AtomicReference;
  7. /**
  8. * @Author hanliukui
  9. * @Date 2021/10/6 23:25
  10. * @Description xxx
  11. */
  12. public class JsonIncludeNonAbsentTest {
  13. @JsonInclude(JsonInclude.Include.NON_NULL)
  14. static class MyObA {
  15. private String title;
  16. private Optional<String> name;
  17. private AtomicReference<Integer> age;
  18. public String getTitle() {
  19. return title;
  20. }
  21. public void setTitle(String title) {
  22. this.title = title;
  23. }
  24. public Optional<String> getName() {
  25. return name;
  26. }
  27. public void setName(Optional<String> name) {
  28. this.name = name;
  29. }
  30. public AtomicReference<Integer> getAge() {
  31. return age;
  32. }
  33. public void setAge(AtomicReference<Integer> age) {
  34. this.age = age;
  35. }
  36. }
  37. public static void main(String[] args) throws JsonProcessingException {
  38. ObjectMapper mapper = new ObjectMapper();
  39. MyObA obA = new MyObA();
  40. obA.setTitle("111");
  41. obA.setName(Optional.empty());
  42. obA.setAge(new AtomicReference<>());
  43. String s = mapper.writeValueAsString(obA);
  44. System.out.println(s);
  45. }
  46. }
  1. {"title":"111","name":{"present":false},"age":null}

NON_ABSENT略为复杂,当实例化的对象有Optional或AtomicReference类型的成员变量时,如果Optional 或 AtomicReference 引用的实例为空,用NON_ABSENT能使该字段不做序列化;

要让Jackson支持Optional特性,必须做两件事:

  1. 在pom.xml中添加以下依赖: ```xml com.fasterxml.jackson.datatype jackson-datatype-jdk8 2.11.0
  1. 2. 在代码中执行以下设置:
  2. ```java
  3. mapper.registerModule(new Jdk8Module());

测试 将NON_NULL改为NON_ABSENT试试:

  1. package com.hlkui.example.jackson.demo.annotation;
  2. import com.fasterxml.jackson.annotation.JsonInclude;
  3. import com.fasterxml.jackson.core.JsonProcessingException;
  4. import com.fasterxml.jackson.databind.ObjectMapper;
  5. import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
  6. import java.util.Optional;
  7. import java.util.concurrent.atomic.AtomicReference;
  8. /**
  9. * @Author hanliukui
  10. * @Date 2021/10/6 23:25
  11. * @Description xxx
  12. */
  13. public class JsonIncludeNonAbsentTest2 {
  14. @JsonInclude(JsonInclude.Include.NON_ABSENT)
  15. static class MyObA {
  16. private String title;
  17. private Optional<String> name;
  18. private AtomicReference<Integer> age;
  19. public String getTitle() {
  20. return title;
  21. }
  22. public void setTitle(String title) {
  23. this.title = title;
  24. }
  25. public Optional<String> getName() {
  26. return name;
  27. }
  28. public void setName(Optional<String> name) {
  29. this.name = name;
  30. }
  31. public AtomicReference<Integer> getAge() {
  32. return age;
  33. }
  34. public void setAge(AtomicReference<Integer> age) {
  35. this.age = age;
  36. }
  37. }
  38. public static void main(String[] args) throws JsonProcessingException {
  39. ObjectMapper mapper = new ObjectMapper();
  40. // Optional 于jdk8 中引入
  41. mapper.registerModule(new Jdk8Module());
  42. MyObA obA = new MyObA();
  43. obA.setTitle("111");
  44. obA.setName(Optional.empty());
  45. obA.setAge(new AtomicReference<>());
  46. String s = mapper.writeValueAsString(obA);
  47. System.out.println(s);
  48. }
  49. }
  1. {"title":"111"}

小结NON_ABSENT的效果:

  1. 自身为null的字段不会被序列化;
  2. Optional类型的字段,如果引用值为null,该字段不会被序列化;
  3. AtomicReference类型的字段,如果引用值为null,该字段不会被序列化;

    NON_EMPTY

    以下情况都不会被序列化:

  4. null

  5. 空字符串
  6. 空集合
  7. 空数组
  8. Optional类型的,其引用为空
  9. AtomicReference类型的,其引用为空

image.png

NON_DEFAULT

对保持默认值的字段不做序列化。
image.png

CUSTOM

相对其他类型,CUSTOM略为复杂,这个值要配合valueFilter属性一起使用;

JsonInclude的value等于CUSTOM时,在序列化的时候会执行CustomFilter的equals方法,该方法的入参就是field0的值,如果equals方法返回true,field0就不会被序列化,如果equals方法返回false时field0才会被序列化

  1. @JsonInclude(value = JsonInclude.Include.CUSTOM,
  2. valueFilter = CustomFilter.class)
  3. private String field0;

来看看CustomFilter类的代码,如下所示,只有equals方法,可见:null、非字符串、长度大于2这三种情况都返回true,也就是说这三种情况下都不会被序列化:

  1. static class CustomFilter {
  2. @Override
  3. public boolean equals(Object obj) {
  4. // null,或者不是字符串就返回true,意味着不被序列化
  5. if(null==obj || !(obj instanceof String)) {
  6. return true;
  7. }
  8. // 长度大于2就返回true,意味着不被序列化
  9. return ((String) obj).length() > 2;
  10. }
  11. }

image.png

USE_DEFAULTS

默认使用时:

  • 类注解和成员变量注解同时存在时,以成员变量注解为准;
  • 如果对应的get方法也使用了JsonInclude注解,并且值是USE_DEFAULTS,此时以类注解为准;

测试1,不使用USE_DEFAULT:

  1. package com.hlkui.example.jackson.demo.annotation;
  2. import com.fasterxml.jackson.annotation.JsonInclude;
  3. import com.fasterxml.jackson.core.JsonProcessingException;
  4. import com.fasterxml.jackson.databind.ObjectMapper;
  5. /**
  6. * @Author hanliukui
  7. * @Date 2021/10/6 23:49
  8. * @Description xxx
  9. */
  10. public class JsonIncludeUseDefaultTest1 {
  11. @JsonInclude(JsonInclude.Include.NON_NULL)
  12. static class MyObD{
  13. @JsonInclude(JsonInclude.Include.NON_EMPTY)
  14. private String title;
  15. private String phone;
  16. public String getTitle() {
  17. return title;
  18. }
  19. public void setTitle(String title) {
  20. this.title = title;
  21. }
  22. public String getPhone() {
  23. return phone;
  24. }
  25. public void setPhone(String phone) {
  26. this.phone = phone;
  27. }
  28. }
  29. public static void main(String[] args) throws JsonProcessingException {
  30. ObjectMapper mapper = new ObjectMapper();
  31. MyObD obD = new MyObD();
  32. obD.setPhone("1008611");
  33. obD.setTitle("");
  34. String s = mapper.writeValueAsString(obD);
  35. System.out.println(s);
  36. }
  37. }
  1. {"phone":"1008611"}

可以发现是属性字段上的注解值 NON_EMPTY 生效了。

测试2,在get 方法上使用USE_DEFAULT:

  1. package com.hlkui.example.jackson.demo.annotation;
  2. import com.fasterxml.jackson.annotation.JsonInclude;
  3. import com.fasterxml.jackson.core.JsonProcessingException;
  4. import com.fasterxml.jackson.databind.ObjectMapper;
  5. /**
  6. * @Author hanliukui
  7. * @Date 2021/10/6 23:49
  8. * @Description xxx
  9. */
  10. public class JsonIncludeUseDefaultTest1 {
  11. @JsonInclude(JsonInclude.Include.NON_NULL)
  12. static class MyObD{
  13. @JsonInclude(JsonInclude.Include.NON_EMPTY)
  14. private String title;
  15. private String phone;
  16. @JsonInclude(JsonInclude.Include.USE_DEFAULTS)
  17. public String getTitle() {
  18. return title;
  19. }
  20. public void setTitle(String title) {
  21. this.title = title;
  22. }
  23. public String getPhone() {
  24. return phone;
  25. }
  26. public void setPhone(String phone) {
  27. this.phone = phone;
  28. }
  29. }
  30. public static void main(String[] args) throws JsonProcessingException {
  31. ObjectMapper mapper = new ObjectMapper();
  32. MyObD obD = new MyObD();
  33. obD.setPhone("1008611");
  34. obD.setTitle("");
  35. String s = mapper.writeValueAsString(obD);
  36. System.out.println(s);
  37. }
  38. }
  1. {"title":"","phone":"1008611"}

此时,类上的注解 NON_NULL 生效。

常用Field注解

JsonProperty

  1. JsonProperty可以作用在成员变量和方法上,作用是在序列化和反序列化操作中指定json字段的名称和序列化的字段顺序
  2. 属性 value=json字段的名称;index = 序列化结果的字段顺序;
  3. 使用 JsonProperty 注解,没有getter 和 setter 方法的私有变量也可以被序列化和反序列化。 ```java package com.hlkui.example.jackson.demo.annotation;

import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature;

/**

  • @Author hanliukui
  • @Date 2021/10/7 11:12
  • @Description xxx */ public class JsonPropertyTest {

    static class JsonPropertyEntry{

    1. @JsonProperty(value = "f1",index = 3)
    2. private String field1;
    3. @JsonProperty(value = "f2",index = 1)
    4. private String field2;
    5. public JsonPropertyEntry(){}
    6. public JsonPropertyEntry(String field1) {
    7. this.field1 = field1;
    8. }
    9. public String getField2() {
    10. return this.field2;
    11. }
    12. public void setField2(String field2){
    13. this.field2=field2;
    14. }
    15. @Override
    16. public String toString() {
    17. return "JsonPropertyEntry{" +
    18. "field1='" + field1 + '\'' +
    19. ", field2='" + field2 + '\'' +
    20. '}';
    21. }

    }

    public static void main(String[] args) throws JsonProcessingException {

    1. // 1.序列化测试
    2. ObjectMapper mapper = new ObjectMapper();
    3. // 美化输出
    4. mapper.enable(SerializationFeature.INDENT_OUTPUT);
    5. JsonPropertyEntry entry = new JsonPropertyEntry("111");
    6. entry.setField2("222");
    7. String s = mapper.writeValueAsString(entry);
    8. System.out.println(s);
    9. // 2. 反序列化测试
    10. JsonPropertyEntry reEntry = mapper.readValue(s, JsonPropertyEntry.class);
    11. System.out.println(reEntry);

    }

}

  1. ```json
  2. {
  3. "f2" : "222",
  4. "f1" : "111"
  5. }
  6. JsonPropertyEntry{field1='111', field2='222'}
  • field1 是私有字段,因为加了注解@JsonProperty ,也可以被序列化和反序列化;
  • field1 设置 index =1,field2 index=3,在序列化结果中,field2 排在前。

JsonIgnore

  1. 作用在成员变量或者方法上;
  2. 指定被注解的变量或者方法不参与序列化和反序列化操作;

JacksonInject

  1. 作用在字段上;
  2. JacksonInject的作用是在反序列化的时候,将配置好的值注入被JacksonInject注解的字段;

    1. static class Test {
    2. private String field0;
    3. @JacksonInject(value = "defaultField1")
    4. private String field1;
    5. @JacksonInject
    6. private String field2;
    7. }
  3. 注入时所需的数据来自哪里呢?如下所示,通过代码配置的,可以指定key对应的注入值,也可以指定类型对应的注入值:

  1. InjectableValues.Std injectableValues = new InjectableValues.Std();
  2. // 指定key为"defaultField1"对应的注入参数
  3. injectableValues.addValue("defaultField1","field1 default value");
  4. // 指定String类型对应的注入参数
  5. injectableValues.addValue(String.class,"String type default value");
  6. ObjectMapper mapper = new ObjectMapper(); // 把注入参数的配置设置给mapper
  7. mapper.setInjectableValues(injectableValues);

反序列化结果如下图,可见field1和field2的值都是被注入的:
image.png

JsonSerialize

  1. JsonSerialize用于序列化场景,被此注解修饰的字段或者get方法会被用于序列化,并且using属性指定了执行序列化操作的类;
  2. 执行序列化操作的类,需要继承自JsonSerializer,如下所示,Date2LongSerialize的作用是将Date类型转成long类型: ```java static class Date2LongSerialize extends JsonSerializer {

    1. @Override
    2. public void serialize(Date value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
    3. gen.writeNumber(value.getTime());
    4. }

    }

  1. 3. Test类的field0字段是私有的,且没有getset方法,但是添加了注释JsonDeserialize就能被反序列化了,并且使用Date2LongSerialize类对将json中的long型转成field0所需的Date型:
  2. ```java
  3. static class Test {
  4. @JsonDeserialize(using = Long2DateDeserialize.class)
  5. private Date field0;
  6. @Override
  7. public String toString() { return "Test{" + "field0='" + field0 + '\'' + '}'; }
  8. }

执行结果如下:
image.png

JsonDeserialize

  1. JsonDeserialize用于反序列化场景,被此注解修饰的字段或者set方法会被用于反序列化,并且using属性指定了执行反序列化操作的类;
  2. 执行反序列化操作的类需要继承自JsonDeserializer,如下所示,Long2DateDeserialize的作用是将Long类型转成field0字段对应的Date类型: ```java static class Long2DateDeserialize extends JsonDeserializer {

    1. @Override
    2. public Date deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
    3. if(null!=p && null!=ctxt && p.getLongValue()>0L ) {
    4. return new Date(p.getLongValue());
    5. }
    6. return null;
    7. }

    }

  1. 测试反序列化,结果如下:<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/1039463/1633578104876-de074e65-a6cc-4123-8c08-ba73ed3b0330.png#clientId=u4dc0c4a3-5b59-4&from=paste&id=u76b02284&margin=%5Bobject%20Object%5D&name=image.png&originHeight=285&originWidth=1108&originalType=url&ratio=1&size=178929&status=done&style=none&taskId=ucefa4f67-c3a7-415d-8506-c1c7e12669b)
  2. <a name="M9UgW"></a>
  3. ### JsonRawValue
  4. 使用该注解的字段或者方法,都会被序列化,但是序列化结果是原始值,例如字符串是不带双引号的:<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/1039463/1633578138243-12e493a7-3681-4293-9e81-58a4daaba8ef.png#clientId=u4dc0c4a3-5b59-4&from=paste&id=u8b22b7eb&margin=%5Bobject%20Object%5D&name=image.png&originHeight=331&originWidth=778&originalType=url&ratio=1&size=183272&status=done&style=none&taskId=u3087acdc-1595-4fea-86c1-635985f9391)
  5. <a name="WSNAH"></a>
  6. ## 常用方法注解
  7. <a name="dcd6b878"></a>
  8. ### 本篇概览
  9. - 本文是《jackson 学习》系列的第八篇,继续学习 jackson 强大的注解能力,本篇学习常用的方法注解,并通过实例来加深印象,下图是常用方法注解的简介:<br />![](https://img-blog.csdnimg.cn/2020090707551444.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2JvbGluZ19jYXZhbHJ5,size_16,color_FFFFFF,t_70#pic_center#id=aFNwa&originHeight=585&originWidth=817&originalType=binary&ratio=1&status=done&style=none)
  10. <a name="JsonValue"></a>
  11. ### JsonValue
  12. 1. 在序列化时起作用,可以用来注解 get 方法或者成员变量;
  13. 1. 一个类中,JsonValue 只允许出现一次;
  14. 1. 如果注解的是 get 方法,那么该方法的返回值就是整个实例的序列化结果;
  15. 1. 如果注解的是成员变量,那么该成员变量的值就是整个实例的序列化结果;
  16. 1. 下面是用来测试的 Pojo 类,JsonValue 注解放在 getField0 方法上,此方法的返回值已经写死了 "abc"
  17. ```java
  18. static class Test {
  19. private String field0;
  20. private String field1;
  21. @JsonValue
  22. public String getField0() { return "abc"; }
  23. public void setField0(String field0) { this.field0 = field0; }
  24. public String getField1() { return field1; }
  25. public void setField1(String field1) { this.field1 = field1; }
  26. }
  1. Test 类的序列化结果如下,即 getField0 方法的返回值:
    Jackson 基本用法 - 图8

JsonCreator

  1. 在反序列化时,当出现有参构造方法时(可能是多个有参构造方法),需要通过 JsonCreator 注解指定反序列化时用哪个构造方法,并且在入参处还要通过 JsonProperty 指定字段关系:
  1. static class Test {
  2. private String field0;
  3. private String field1;
  4. public Test(String field0) {
  5. this.field0 = field0;
  6. }
  7. // 通过JsonCreator指定反序列化的时候使用这个构造方法
  8. // 通过JsonProperty指定字段关系
  9. @JsonCreator
  10. public Test(@JsonProperty("field0") String field0,
  11. @JsonProperty("field1") String field1) {
  12. this.field0 = field0;
  13. this.field1 = field1;
  14. }
  15. @Override
  16. public String toString() {
  17. return "Test{" +
  18. "field0='" + field0 + '\'' +
  19. ", field1='" + field1 + '\'' +
  20. '}';
  21. }
  22. }
  1. 反序列化结果如下:
    Jackson 基本用法 - 图9

JsonSetter

  1. JsonSetter 注解在 set 方法上,被用来在反序列化时指定 set 方法对应 json 的哪个属性;
  2. JsonSetter 源码中,推荐使用 JsonProperty 来取代 JsonSetter:
    Jackson 基本用法 - 图10
  3. 测试代码和结果如下,可见反序列化时,是按照 JsonSetter 的 value 去 json 中查找属性的:
    Jackson 基本用法 - 图11

JsonGetter

  1. JsonGetter 只能作为方法注解;
  2. 在序列化时,被 JsonGetter 注解的 get 方法,对应的 json 字段名是 JsonGetter 的 value;
  3. JsonGetter 源码中,推荐使用 JsonProperty 来取代 JsonGetter:
    Jackson 基本用法 - 图12
  4. 测试代码和结果如下,可见序列化时 JsonGetter 的 value 会被作为 json 字段名:
    Jackson 基本用法 - 图13

JsonAnyGetter

  1. JsonAnyGetter 的作用有些特别:在序列化时,用 Map 对象的键值对转成 json 的字段和值;
  2. 理解 JsonAnyGetter 最好的办法,是对比使用前后序列化结果的变化,先来看以下这段代码,是没有 JsonAnyGetter 注解的,Test 有两个成员变量,其中 map 字段是 HashMap 类型的:
  1. package com.bolingcavalry.jacksondemo.annotation.methodannotation;
  2. import com.fasterxml.jackson.annotation.JsonAnyGetter;
  3. import com.fasterxml.jackson.databind.ObjectMapper;
  4. import com.fasterxml.jackson.databind.SerializationFeature;
  5. import java.util.HashMap;
  6. import java.util.Map;
  7. public class JsonAnySetterSerialization {
  8. static class Test {
  9. private String field0;
  10. private Map<String, Object> map;
  11. public String getField0() { return field0; }
  12. public void setField0(String field0) { this.field0 = field0; }
  13. public void setMap(Map<String, Object> map) { this.map = map; }
  14. public Map<String, Object> getMap() { return map; }
  15. }
  16. public static void main(String[] args) throws Exception {
  17. ObjectMapper mapper = new ObjectMapper();
  18. // 美化输出
  19. mapper.enable(SerializationFeature.INDENT_OUTPUT);
  20. // 新增一个HashMap,里面放入两个元素
  21. Map<String, Object> map = new HashMap<>();
  22. map.put("aaa", "value_aaa");
  23. map.put("bbb", "value_bbb");
  24. Test test = new Test();
  25. test.setField0("000");
  26. // map赋值给test.map
  27. test.setMap(map);
  28. System.out.println(mapper.writeValueAsString(test));
  29. }
  30. }
  1. 上述代码的执行结果如下,其实很好理解,就是 field0 和 map 两个字段而已:
  1. {
  2. "field0" : "000",
  3. "map" : {
  4. "aaa" : "value_aaa",
  5. "bbb" : "value_bbb"
  6. }
  7. }
  1. 接下来,对上述代码做一处改动,如下图红框所示,给 getMap 方法增加 JsonAnyGetter 注解:
    Jackson 基本用法 - 图14
  2. 修改后的执行结果如下,原来的 map 字段没有了,map 内部的所有键值对都成了 json 的字段:
  1. {
  2. "field0" : "000",
  3. "aaa" : "value_aaa",
  4. "bbb" : "value_bbb"
  5. }
  1. 至此,可以品味出 JsonAnyGetter 的作用了:序列化时,将 Map 中的键值对全部作为 JSON 的字段输出;

JsonAnySetter

  1. 弄懂了前面的 JsonAnyGetter,对于 JsonAnySetter 的作用想必您也能大致猜到:反序列化时,对 json 中不认识的字段,统统调用 JsonAnySetter 注解修饰的方法去处理;
  2. 测试的代码如下,Test 类的 setValue 方法被 JsonAnySetter 注解,在反序列化时,json 中的 aaa 和 bbb 字段,都会交给 setValue 方法处理,也就是放入 map 中:
  1. package com.bolingcavalry.jacksondemo.annotation.methodannotation;
  2. import com.fasterxml.jackson.annotation.JsonAnySetter;
  3. import com.fasterxml.jackson.annotation.JsonCreator;
  4. import com.fasterxml.jackson.annotation.JsonProperty;
  5. import com.fasterxml.jackson.databind.ObjectMapper;
  6. import java.util.HashMap;
  7. import java.util.Map;
  8. public class JsonAnySetterDeserialization {
  9. static class Test {
  10. private String field0;
  11. private Map<String, Object> map = new HashMap<>();
  12. @JsonAnySetter
  13. public void setValue(String key, Object value) {
  14. map.put(key, value);
  15. }
  16. @Override
  17. public String toString() {
  18. return "Test{" +
  19. "field0='" + field0 + '\'' +
  20. ", map=" + map +
  21. '}';
  22. }
  23. }
  24. public static void main(String[] args) throws Exception {
  25. String jsonStr = "{\n" +
  26. " \"field0\" : \"000\",\n" +
  27. " \"aaa\" : \"value_aaa\",\n" +
  28. " \"bbb\" : \"value_bbb\"\n" +
  29. "}";
  30. System.out.println(new ObjectMapper().readValue(jsonStr, Test.class));
  31. }
  32. }
  1. 执行结果如下,可见 aaa、bbb 都被放入了 map 中:
  1. Test{field0='null', map={aaa=value_aaa, field0=000, bbb=value_bbb}}
  1. 另外 JsonAnySetter 还可以作用在成员变量上,上面的代码中,去掉 setValue 方法,在成员变量 map 上增加 JsonAnySetter 注解,修改后如下,执行结果也是一模一样的:
  1. static class Test {
  2. private String field0;
  3. @JsonAnySetter
  4. private Map<String, Object> map = new HashMap<>();
  5. @Override
  6. public String toString() {
  7. return "Test{" +
  8. "field0='" + field0 + '\'' +
  9. ", map=" + map +
  10. '}';
  11. }
  12. }
  1. 注意,JsonAnySetter 作用在成员变量上时,该成员变量必须是 java.util.Map 的实现类;