写在前面

Jackson Github 地址:https://github.com/FasterXML

Jackson 是干什么的也没必要过多说明,相信各位应该都很熟悉。Jackson 和 GJson 以及 Fastjson 的性能比较啊,本篇也不做说明。因为啰里啰嗦的实在没意义,有兴趣的自行百度谷歌吧。

Jaskson 在实际中主要有两方面的应用:JSON 转换以及 XML 的转换,本篇介绍 JSON 字符串和对象之间的转换。

想要使用 Jaskson 需要在 pom 文件中引入它的依赖:

  1. <dependency>
  2. <groupId>com.fasterxml.jackson.core</groupId>
  3. <artifactId>jackson-databind</artifactId>
  4. <version>${jaskon.version}</version>
  5. </dependency>
说明


在实际使用中我们只需要引入 jackson-databind 这个依赖即可,因为该依赖内嵌了 jaskson 核心依赖包和注解依赖包,在实际中基本上已经满足我们的需要了(如下依赖关系)
image.png

想要使用 Jackson 的 JSON 转换 API 只需要声明一个 ObjectMapper 对象即可:

  1. ObjectMapper objectMapper = new ObjectMapper();
说明


ObjectMapper 是线程安全的类,所以为了运行效率我们通常声明一个全局的 Jackson 对象即可,如下:
```java

public final class JacksonUtil {

  1. private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
  2. public static ObjectMapper getObjectMapper () {
  3. return OBJECT_MAPPER;
  4. }

}

  1. |
  2. <a name="DWHHQ"></a>
  3. # 对象转JSON字符串
  4. 将一个对象转JSON字符串(通常称为对象序列化)主要使用 `ObjectMapper` `writeValueAsString()` 方法,将需要转换的对象转入即可(可以是一个普通的类对象,也可以是一个集合和 Map 对象)如下:
  5. ```java
  6. public String writeValueAsString(Object value);

当然,Jackson 除了能够将对象转换为 Json 字符串还能转换为二进制流或输出到文件,见下文的序列化反序列化扩展

现在来看下如何将对象转换为 JSON 字符串,声明一个 User 类:

  1. public class User {
  2. private String name;
  3. private Integer age;
  4. private LocalDate date;
  5. private LocalTime time;
  6. private LocalDateTime dateTime;
  7. private List<String> tags;
  8. // Getter And Setter, 下文略...
  9. }

直接使用 ObjectMapper#writeValueAsString() 方法就能够实现 JSON 转换:

  1. ObjectMapper objectMapper = new ObjectMapper();
  2. User user = User.builder().name("张三").age(18).build();
  3. // user 对象转 JSON 字符串
  4. String json = objectMapper.writeValueAsString(user);
  5. System.out.println(json);

输出结果:

  1. {
  2. "name": "张三",
  3. "age": 18,
  4. "date": null,
  5. "time": null,
  6. "dateTime": null,
  7. "tags": null
  8. }

忽略 NULL 字段

上面的输出结果中包含了值为 NULL 的字段,如果想要忽略值为 NULL 的字段可以使用 ObjectMapper#setSerializationInclusion() API,如下:

  1. public ObjectMapper setSerializationInclusion(JsonInclude.Include incl);

JsonInclude.Include 枚举类定义如下:

  1. public enum Include {
  2. /**
  3. * 包含所有字段
  4. */
  5. ALWAYS,
  6. /**
  7. * 忽略值为 NULL 的字段
  8. */
  9. NON_NULL,
  10. NON_ABSENT,
  11. NON_EMPTY,
  12. NON_DEFAULT,
  13. CUSTOM,
  14. USE_DEFAULTS;
  15. }

如果不显示的设置 JsonInclude.Include ,它的默认值是 ALWAYS

如果要忽略值为 NULL 的字段,直接设置枚举值 JsonInclude.Include.NON_NULL 即可,如下:

  1. ObjectMapper objectMapper = new ObjectMapper();
  2. // 忽略值为 NULL 的字段
  3. objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
  4. User user = User.builder().name("张三").age(18).build();
  5. String json = objectMapper.writeValueAsString(user);
  6. System.out.println(json);

输出结果:

  1. {
  2. "name": "张三",
  3. "age": 18
  4. }
注意
如果不显示的调用 ObjectMapper#setSerializationInclusion() 方法进行设置 JsonInclude.Include ,那么它的默认值是 ALWAYS,即默认会序列化类中的所有属性字段(不包括 static)。

忽略指定字段

在序列化时想要忽略类中的某个字段主要有如下几种形式:

使用 @JsonIgnoreProperties 注解

com.fasterxml.jackson.annotation.JsonIgnoreProperties#value 接受一个数组字符串,每个字符串代表类中的一个属性字段,将想要忽略的字段以字符串数组的形式写上即可,如下:

  1. @JsonIgnoreProperties({"name", "age"})
  2. public class User {
  3. private String name;
  4. private Integer age;
  5. private LocalDate date;
  6. private LocalTime time;
  7. private LocalDateTime dateTime;
  8. private List<String> tags;
  9. }

@JsonIgnoreProperties 注解不仅可以作用于类上,还可以直接写在某个属性上,表示在序列化反序列化是忽略该字段:

  1. public class User {
  2. @JsonIgnoreProperties
  3. private String name;
  4. private Integer age;
  5. private LocalDate date;
  6. private LocalTime time;
  7. private LocalDateTime dateTime;
  8. private List<String> tags;
  9. }

使用 FilterProvider

FilterProvider 是最不推荐的使用方式,因为冗余性太强。看下使用示例吧:

  1. ObjectMapper objectMapper = new ObjectMapper();
  2. // 除了 name 字段都忽略
  3. SimpleBeanPropertyFilter propertyFilter = SimpleBeanPropertyFilter.filterOutAllExcept("name");
  4. filterProvider.addFilter("userPropertyFilter", propertyFilter);
  5. objectMapper.setFilterProvider(filterProvider);
  6. User user = User.builder().name("张三").age(18).build();
  7. String json = objectMapper.writeValueAsString(user);
  8. System.out.println(json);

注意 SimpleBeanPropertyFilter.filterOutAllExcept() API,该 API 指的是除了指定字段都忽略。即除了我们输入的 name 字段都被忽略掉,感觉有点反人类。

之后我们设置了一个过滤器ID:userPropertyFilter,我们还需要在 User 类上使用 @JsonFilter("userPropertyFilter") 注解,值就是我们上面设置的值:

  1. @JsonFilter("userPropertyFilter")
  2. public class User {
  3. // ...
  4. }

现在运行才能达到我们的效果:

  1. {"name":"张三"}

但,怎么说了…… 这个 API 用起来很难受反正。

Java8 日期序列化问题

现在来再看一个示例,代码如下:

  1. ObjectMapper objectMapper = new ObjectMapper();
  2. // 注意 date 字段值
  3. User user = User.builder().name("张三").age(18).date(LocalDate.now()).build();
  4. String json = objectMapper.writeValueAsString(user);
  5. System.out.println(json);

输出如下:

  1. {
  2. "name": "张三",
  3. "age": 18,
  4. "date": {
  5. "year": 2021,
  6. "month": "JULY",
  7. "monthValue": 7,
  8. "dayOfMonth": 26,
  9. "chronology": {
  10. "id": "ISO",
  11. "calendarType": "iso8601"
  12. },
  13. "era": "CE",
  14. "dayOfYear": 207,
  15. "dayOfWeek": "MONDAY",
  16. "leapYear": false
  17. }
  18. }

你会发现 date 字段输出的日期感觉有点反人类是不?这是 Jackson 对 Java8 日期序列化的问题。具体见下文的 Java8 日期格式问题

对象序列化到本地文件

Jackson 不仅仅能够将一个对象序列化成一个 Json 字符串,还能够将序列化的结果输出到本地文件、IO流以及二进制数据等等。

ObjectMapper 提供了如下方法:

  1. public void writeValue(DataOutput out, Object value);
  2. public void writeValue(Writer w, Object value);
  3. public byte[] writeValueAsBytes(Object value);
  4. public void writeValue(File resultFile, Object value);

对象系列化输出到本地文件示例:

  1. ObjectMapper objectMapper = new ObjectMapper();
  2. File file = new File("/Users/Desktop/test.txt");
  3. User user = User.builder().name("张三").age(18).date(LocalDate.now()).build();
  4. objectMapper.writeValue(file, user);

JSON字符串转对象

JSON字符串转对象(通常称为反序列化)主要使用的是 ObjectMapper#readValue 方法。Jackson 提供了多种重载方法,这些方法都定义在 ObjectMapper 类中,下面是 String Json 字符串的三种重载 API:

  1. public <T> T readValue(String content, Class<T> valueType);
  2. public <T> T readValue(String content, JavaType valueType);
  3. public <T> T readValue(String content, TypeReference valueTypeRef);

ObjectMapper 类中除了定义 String 的重载 API 还定义了多种流的重载 API,见下文。

上面方法中的 valueType 就是我们要转换的对象类型,可以看到有两种重载方式,分别是 ClassJavaType,单对象转换我使用 Class 作为参数的比较多。

TypeReference 相比较 Class 更加强大,因为 Class 只能接受单类型对象,如果想要将 Json 字符串转换为集合或者 Map 对象 Class 是实现不了的,这种情况下只能使用 TypeReference。下面还是具体说明:

JSON字符串转 Class

话不多说,直接上代码:

  1. String jsonStr = "{\"name\":\"张三\",\"age\":18,\"date\":null,\"time\":null,\"dateTime\":null,\"tags\":null}";
  2. ObjectMapper objectMapper = new ObjectMapper();
  3. User user = objectMapper.readValue(jsonStr, User.class);
  4. System.out.println(user);

看截图:

image.png

看控制台,转换成功!

上面的示例使用的是 Class 重载方法 readValue(String content, Class<T> valueType)

另外我们还可以使用 TypeReference 重载方法 readValue(String content, TypeReference valueTypeRef) 来实现相同的效果:

  1. String jsonStr = "{\"name\":\"张三\",\"age\":18,\"date\":null,\"time\":null,\"dateTime\":null,\"tags\":null}";
  2. ObjectMapper objectMapper = new ObjectMapper();
  3. // 注意 TypeReference 的用法
  4. User user = objectMapper.readValue(jsonStr, new TypeReference<User>(){});
  5. System.out.println(user);

JSON 字符串转 List

在实际使用中我们比较多的应用就是 JSON 数组形式的字符串转集合,想要解决这个问题就不能使用 Class 重载方法了,而要使用 TypeReference 重载方法。看下示例:

  1. String jsonStr = "[{\"name\":\"张三\",\"age\":18,\"date\":null,\"time\":null,\"dateTime\":null,\"tags\":null},{\"name\":\"李四\",\"age\":20,\"date\":null,\"time\":null,\"dateTime\":null,\"tags\":null}]";
  2. ObjectMapper objectMapper = new ObjectMapper();
  3. // 注意 TypeReference 的用法
  4. List<User> user = objectMapper.readValue(jsonStr, new TypeReference<List<User>>(){});
  5. System.out.println(user);

截图如下:

image.png

JSON 字符串转 Map

同样了,字符串转 Map 也不能少,转 Map 我们同样需要使用 TypeReference API。

记住一点,**Class** API 能解决的 **TypeReference** 都能解决,**Class** 解决不了的,**TypeReference** 还能解决~

直接看示例:

  1. String jsonStr = "[{\"name\":\"张三\",\"age\":18,\"date\":null,\"time\":null,\"dateTime\":null,\"tags\":null},{\"name\":\"李四\",\"age\":20,\"date\":null,\"time\":null,\"dateTime\":null,\"tags\":null}]";
  2. ObjectMapper objectMapper = new ObjectMapper();
  3. // 注意 TypeReference 的用法
  4. Map<String, String> map = objectMapper.readValue(jsonStr, new TypeReference<Map<String, String>>() {});
  5. System.out.println(user);

截图如下:

image.png

Java8 日期反序列化问题

TypeReference 看起来很强,但是有个数据它是解决不了的,那就是 Java8 的日期 API。比如下面的 JSON:

  1. {
  2. "name": "张三",
  3. "age": 18,
  4. "date": null,
  5. "time": null,
  6. "dateTime": "2021-07-26 18:35:02",
  7. "tags": null
  8. }

当你尝试转换成 User 对象时会提示如下错误:

image.png

归根结底,这是 Jackson 对 Java8 日期格式化得问题,下面就来看下 Jaskson 如果解决 Java8 日期格式问题

文件JSON转对象

Jackson 不仅能够将普通的 JSON 字符串反序列化为对象,还能够直接从文件中读取 JSON 内容反序列化成对象。

ObjectMapper 提供的 File 三种重载方法:

  1. public <T> T readValue(File src, Class<T> valueType);
  2. public <T> T readValue(File src, JavaType valueType);
  3. public <T> T readValue(File src, TypeReference valueTypeRef);

用法与 JSON字符串转对象 一致,看下下面的示例:

  1. ObjectMapper objectMapper = new ObjectMapper();
  2. User user = objectMapper.readValue(new File("/Users/Desktop/test.json"), User.class);

需要说明的是文件并不限制一定是 json 文件,只要文件内容是 JSON 格式就能够正常解析,比如 txt 文件:

  1. ObjectMapper objectMapper = new ObjectMapper();
  2. User user = objectMapper.readValue(new File("/Users/Desktop/test.txt"), User.class);

网络文件JSON转对象

Jackson 同样提供了从网络文件(URL)读取 JSON 内容的 API,ObjectMapper URL 的三种重载方法定义如下:

  1. public <T> T readValue(URL src, Class<T> valueType);
  2. public <T> T readValue(URL src, JavaType valueType);
  3. public <T> T readValue(URL src, TypeReference valueTypeRef);

直接看示例:

  1. ObjectMapper objectMapper = new ObjectMapper();
  2. User user = objectMapper.readValue(new URL("file:/Users/Desktop/test.txt"), User.class);

二进制流转对象

既然能够从文件和网络中获取 JSON 内容,肯定也能够解析 IO 流:

  1. public <T> T readValue(InputStream src, Class<T> valueType);
  2. public <T> T readValue(InputStream src, JavaType valueType);
  3. public <T> T readValue(InputStream src, TypeReference valueTypeRef);

示例:

  1. ObjectMapper objectMapper = new ObjectMapper();
  2. User user = objectMapper.readValue(new FileInputStream(new File("/Users/Desktop/test.txt")), User.class);

Java8 日期格式问题

先看下示例:

  1. ObjectMapper objectMapper = new ObjectMapper();
  2. User user = User.builder().name("张三").age(18)
  3. .time(LocalTime.now())
  4. .date(LocalDate.now())
  5. .dateTime(LocalDateTime.now())
  6. .build();
  7. String json = objectMapper.writeValueAsString(user);

输出结果为:

  1. {
  2. "name": "张三",
  3. "age": 18,
  4. "date": {
  5. "year": 2021,
  6. "month": "JULY",
  7. "era": "CE",
  8. "dayOfYear": 207,
  9. "dayOfWeek": "MONDAY",
  10. "leapYear": false,
  11. "dayOfMonth": 26,
  12. "monthValue": 7,
  13. "chronology": {
  14. "id": "ISO",
  15. "calendarType": "iso8601"
  16. }
  17. },
  18. "time": {
  19. "hour": 13,
  20. "minute": 38,
  21. "second": 52,
  22. "nano": 514000000
  23. },
  24. "dateTime": {
  25. "dayOfYear": 207,
  26. "dayOfWeek": "MONDAY",
  27. "month": "JULY",
  28. "dayOfMonth": 26,
  29. "year": 2021,
  30. "monthValue": 7,
  31. "hour": 13,
  32. "minute": 38,
  33. "second": 52,
  34. "nano": 515000000,
  35. "chronology": {
  36. "id": "ISO",
  37. "calendarType": "iso8601"
  38. }
  39. },
  40. "tags": null
  41. }

注意看 datetimedateTime 字段,这个输出信息与我们预想的似乎不太一样。

解决该问题主要有两种方式:使用 Jackson 的 JavaTimeModule 或者 使用自定义序列化反序列化方式。分别来看下:

使用 JavaTimeModule 方式(推荐)

想要使用 JavaTimeModule 解决 Java8 日期格式化问题我们需要引入 Jackson 的 jsr310 依赖:

  1. <dependency>
  2. <groupId>com.fasterxml.jackson.datatype</groupId>
  3. <artifactId>jackson-datatype-jsr310</artifactId>
  4. <version>${jaskon.version}</version>
  5. </dependency>

这样,我们就能够创建一个 JavaTimeModule 对象了:

  1. JavaTimeModule javaTimeModule = new JavaTimeModule();

当然我们还是要做些相应的配置才行,不过最终这个 JavaTimeModule 对象是要注册到 ObjectMapper 对象中的,如下:

  1. public ObjectMapper registerModule(Module module);

所以,为了方便我们还是创建一个静态方法来配置 JavaTimeModule,如下:

  1. private static void configureObjectMapper4Jsr310(ObjectMapper objectMapper) {
  2. JavaTimeModule javaTimeModule = new JavaTimeModule();
  3. // config JavaTimeModule ...
  4. objectMapper.registerModule(javaTimeModule);
  5. }

这样做的主要原因是 JavaTimeModule 是 Module 的一个子实现,也就是说在实际使用中我们可能还会为其他子实现进行定制化配置。通过将配置进行公共提取,当其他 ObjectMapper 也需要相应配置时直接调用该方法进行注册即可。

现在就来配置 JavaTimeModule 来解决 Java8 日期格式的问题。

配置序列化和反序列化

先看下 JavaTimeModule 类继承图:

JavaTimeModule.png

配置日期格式问题我们主要借助它的两个方法:

  1. // 序列化使用
  2. public <T> SimpleModule addSerializer(Class<? extends T> type, JsonSerializer<T> ser);
  3. // 反序列化使用
  4. public <T> SimpleModule addDeserializer(Class<T> type, JsonDeserializer<? extends T> deser);

这两个方法都是在父类中的 SimpleModule 中定义。

形参 type 指的是我们要序列化的类型,如 LocalDateTime.class

形参 serdeser 指的是我们序列化和反序列化的具体实现方式,比如我们要配置 LocalDateTime.class 的序列化和反序列化方法,需要传递的序列化和反序列化对象就是 LocalDateTimeSerializerLocalDateTimeDeserializer

具体就不做过多说明了,现在来看下该具体配置吧,直接上代码:

  1. private static void configureObjectMapper4Jsr310(ObjectMapper objectMapper) {
  2. JavaTimeModule javaTimeModule = new JavaTimeModule();
  3. // LocalTime 序列化和反序列化配置
  4. DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("HH:mm:ss");
  5. javaTimeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(timeFormatter));
  6. javaTimeModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(timeFormatter));
  7. // LocalDate 序列化和反序列化配置
  8. DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
  9. javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(dateFormatter));
  10. javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(dateFormatter));
  11. // LocalDateTime 序列化和反序列化配置
  12. DateTimeFormatter datetimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
  13. javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(datetimeFormatter));
  14. javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(datetimeFormatter));
  15. objectMapper.registerModule(javaTimeModule);
  16. }

这样,Java8 日期格式化的配置就完成了。

日期序列化测试

  1. ObjectMapper objectMapper = new ObjectMapper();
  2. // 配置 jsr310 日期序列化和反序列化问题
  3. configureObjectMapper4Jsr310(objectMapper);
  4. User user = User.builder().name("张三").age(18)
  5. .time(LocalTime.now())
  6. .date(LocalDate.now())
  7. .dateTime(LocalDateTime.now())
  8. .build();
  9. String json = objectMapper.writeValueAsString(user);

输出结果:

  1. {
  2. "name": "张三",
  3. "age": 18,
  4. "date": "2021-07-26",
  5. "time": "15:27:16",
  6. "dateTime": "2021-07-26 15:27:16",
  7. "tags": null
  8. }

这回日期就是我们想要的输出格式了~

日期反序列化测试

  1. String jsonStr = "{\"name\":\"张三\",\"age\":18,\"date\":null,\"time\":null,\"dateTime\":\"2021-07-26 18:35:02\",\"tags\":null}";
  2. ObjectMapper objectMapper = new ObjectMapper();
  3. // 配置 jsr310 日期序列化和反序列化问题
  4. configureObjectMapper4Jsr310(objectMapper);
  5. User user = objectMapper.readValue(jsonStr, User.class);
  6. System.out.println(user);

看下截图:

image.png

这就完结解决 Java8 日期格式的问题了~

自定义序列化和反序列化方式

注意,这种配置方式与上面的 JavaTimeModule 本质上是一样的。主要是扩展 com.fasterxml.jackson.databind.JsonSerializercom.fasterxml.jackson.databind.JsonDeserializer 来实现自定义某个类的序列化问题。

话不多说,直接上代码:

日期序列化配置

序列化 LocalDate:

  1. public class LocalDateJsonSerializer extends JsonSerializer<LocalDate> {
  2. @Override
  3. public void serialize(LocalDate date, JsonGenerator gen, SerializerProvider serializers) throws IOException {
  4. gen.writeString(date.format(DateTimeFormatter.ofPattern("yyyy-MM-dd", Locale.CHINA)));
  5. }
  6. }

序列化 LocalTime:

  1. public class LocalTimeJsonSerializer extends JsonSerializer<LocalTime> {
  2. @Override
  3. public void serialize(LocalTime time, JsonGenerator gen, SerializerProvider serializers) throws IOException {
  4. gen.writeString(time.format(DateTimeFormatter.ofPattern("HH:mm:ss", Locale.CHINA)));
  5. }
  6. }

序列化 LocalDateTime:

  1. public class LocalTimeJsonSerializer extends JsonSerializer<LocalDateTime> {
  2. @Override
  3. public void serialize(LocalDateTime datetime, JsonGenerator gen, SerializerProvider serializers) throws IOException {
  4. gen.writeString(datetime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss", Locale.CHINA)));
  5. }
  6. }

日期反序列化配置

反序列化LocalDate:

  1. public class LocalDateJsonDeserializer extends JsonDeserializer<LocalDate> {
  2. @Override
  3. public LocalDate deserialize(JsonParser parser, DeserializationContext context) throws IOException {
  4. return LocalDate.parse(parser.getText(), DateTimeFormatter.ofPattern("yyyy-MM-dd"));
  5. }
  6. }

反序列化LocalTime:

  1. public class LocalTimeJsonDeserializer extends JsonDeserializer<LocalTime> {
  2. @Override
  3. public LocalTime deserialize(JsonParser parser, DeserializationContext context) throws IOException {
  4. return LocalTime.parse(parser.getText(), DateTimeFormatter.ofPattern("HH:mm:ss"));
  5. }
  6. }

反序列化LocalDateTime:

  1. public class LocalDateTimeJsonDeserializer extends JsonDeserializer<LocalDateTime> {
  2. @Override
  3. public LocalDateTime deserialize(JsonParser parser, DeserializationContext context) throws IOException {
  4. return LocalDateTime.parse(parser.getText(), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
  5. }
  6. }

日期序列化测试

想要使用上面我们自定义的序列化和反序列化配置主要有两种方式。第一种比较简单,借助 Jackson 提供的 @JsonSerialize@JsonDeserialize 注解。如下:

  1. public class User {
  2. private String name;
  3. private Integer age;
  4. @JsonSerialize(using = LocalDateJsonSerializer.class)
  5. @JsonDeserialize(using = LocalDateJsonDeserializer.class)
  6. private LocalDate date;
  7. @JsonSerialize(using = LocalTimeJsonSerializer.class)
  8. @JsonDeserialize(using = LocalTimeJsonDeserializer.class)
  9. private LocalTime time;
  10. @JsonSerialize(using = LocalDateTimeJsonSerializer.class)
  11. @JsonDeserialize(using = LocalDateTimeJsonDeserializer.class)
  12. private LocalDateTime dateTime;
  13. private List<String> tags;
  14. }

测试一下:

  1. ObjectMapper objectMapper = new ObjectMapper();
  2. User user = User.builder().name("张三").age(18)
  3. .time(LocalTime.now())
  4. .date(LocalDate.now())
  5. .dateTime(LocalDateTime.now())
  6. .build();
  7. String json = objectMapper.writeValueAsString(user);

输出结果:

  1. {
  2. "name": "张三",
  3. "age": 18,
  4. "date": "2021-07-26",
  5. "time": "15:48:59",
  6. "dateTime": "2021-07-26 15:48:59",
  7. "tags": null
  8. }

看起来似乎有点啰嗦,可能会有同学问直接使用 @JsonFormat 注解不行吗?如下:

  1. public class User {
  2. private String name;
  3. private Integer age;
  4. @JsonFormat(pattern = "yyyy-MM-dd")
  5. private LocalDate date;
  6. private LocalTime time;
  7. private LocalDateTime dateTime;
  8. private List<String> tags;
  9. }

抱歉,不行的。@JsonFormat 配置形式只能格式化 java.util.Date 日期格式~

实际上,还有一种使用形式。不过这种主要是在 SpringMVC 的消息转换器中使用。因为需要借助 SpringMVC 的 Jackson 消息转换器 org.springframework.http.converter.json.Jackson2ObjectMapperBuilder。如下示例:

  1. private static void configureObjectMapper4Jsr310(ObjectMapper objectMapper) {
  2. Jackson2ObjectMapperBuilder mapperBuilder = new Jackson2ObjectMapperBuilder();
  3. mapperBuilder.serializerByType(LocalDate.class, new LocalDateJsonSerializer());
  4. mapperBuilder.serializerByType(LocalTime.class, new LocalTimeJsonSerializer());
  5. mapperBuilder.serializerByType(LocalDateTime.class, new LocalDateTimeJsonSerializer());
  6. mapperBuilder.deserializerByType(LocalDate.class, new LocalDateJsonDeserializer());
  7. mapperBuilder.deserializerByType(LocalTime.class, new LocalTimeJsonDeserializer());
  8. mapperBuilder.deserializerByType(LocalDateTime.class, new LocalDateTimeJsonDeserializer());
  9. mapperBuilder.configure(objectMapper);
  10. }

反序列化就不做测试了~

使用 ObjectNode

有时候我们可能需要创建一个对象具有 Map 能够 put 一个 Key - Value 的功能,这个时候我们就需要使用 ObjectNode 对象:

  1. ObjectNode objectNode = objectMapper.createObjectNode();

需要说明一点:ObjectnNode 继承至 JsonNode。

有了 ObjectNode 对象我们就能够调用它的相关 put 对象进行设置一个 Key -Value 了:

image.png

看下示例:

  1. ObjectMapper objectMapper = new ObjectMapper();
  2. ObjectNode objectNode = objectMapper.createObjectNode();
  3. objectNode.put("money", new BigDecimal("100.00"));

不过在获取值得时候需要注意了,调用 get(Key) 方法返回的是一个 JsonNode 对象:

  1. JsonNode jsonNode = objectNode.get("money");

想要获取具体的数据类型需要相应的转换,比如上面设置的 money 是个 BigDecimal 类型对象,就需要调用 Decimal 方法进行转换:

  1. JsonNode jsonNode = objectNode.get("money");
  2. BigDecimal money = jsonNode.decimalValue();

JsonNode 还有一个用法,就是将一个对象转换成 JsonNode:

  1. ObjectMapper objectMapper = new ObjectMapper();
  2. User user = User.builder().name("张三").age(18).date(LocalDate.now()).build();
  3. JsonNode jsonNode = objectMapper.valueToTree(user);

同理,也能够将一个 Json 字符串转换成 JsonNode 对象:

  1. ObjectMapper objectMapper = new ObjectMapper();
  2. String jsonStr = "{\"name\":\"张三\",\"age\":18,\"date\":null,\"time\":null,\"dateTime\":null,\"tags\":null}";
  3. JsonNode jsonNode = objectMapper.readTree(jsonStr);

特别强调的一点是:ObjectMapper 通常都是声明为全局对象,这个全局对象都是做过相应配置的。所以在创建 ObjectNode 时最好使用下面的方式:

  1. ObjectMapper objectMapper = new ObjectMapper();
  2. // config objectMapper
  3. ObjectNode objectNode = new ObjectNode(objectMapper.getNodeFactory());

使用 ArrayNode

ObjectNode 都有了怎么能没有 ArrayNode?

ObjectNode 的功能类似于 Map,而 ArrayNode 就是 List 了。

创建 ArrayNode 对象:

  1. ArrayNode arrayNode = objectMapper.createArrayNode();

推荐方式:

  1. ArrayNode arrayNode = new ArrayNode(objectMapper.getNodeFactory())

具体使用也就没必要介绍了。

完结,撒花~