轩辕李的博客 轩辕李的博客
首页
  • Java
  • Spring
  • 其他语言
  • 工具
  • HTML&CSS
  • JavaScript
  • 分布式
  • 代码质量管理
  • 基础
  • 操作系统
  • 计算机网络
  • 编程范式
  • 安全
  • 中间件
  • 心得
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

轩辕李

勇猛精进,星辰大海
首页
  • Java
  • Spring
  • 其他语言
  • 工具
  • HTML&CSS
  • JavaScript
  • 分布式
  • 代码质量管理
  • 基础
  • 操作系统
  • 计算机网络
  • 编程范式
  • 安全
  • 中间件
  • 心得
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • Java

    • 核心

    • 并发

    • 经验

    • JVM

    • 企业应用

      • Freemarker模板
      • Servlet与JSP指南
      • Java日志系统
      • Java JSON处理
        • 前言
        • Java中的JSON应用场景
        • Java中的JSON库概述
          • Gson
          • Fastjson
          • Fastjson2
        • Jackson的使用
          • 1. 添加Jackson依赖
          • 2. 创建ObjectMapper对象
          • 3. JSON与Java对象的相互转换
          • 4. JSON与Java集合的相互转换
          • 5. 复杂对象的处理
          • 6. JsonNode的用法
          • 7. 美化JSON输出
          • 8. JSON处理的行为和特性配置
          • 9. 时间处理
          • 配置ObjectMapper日期格式
          • 使用@JsonFormat格式化日期
          • 自定义日期序列化器
          • 使用Jackson序列化Joda-Time
          • 使用Jackson序列化Java 8日期
          • 10. 自定义序列化和反序列化
          • Jackson的datatype模块
          • 注册自己的Module
          • 11. Jackson的注解
          • 12. 对其他数据格式的支持
          • 13. 其他
        • 总结
      • Java XML处理
      • Java对象池技术
      • Java程序的单元测试(Junit)
      • Thymeleaf官方文档(中文版)
      • Mockito应用
      • Java中的空安全
  • Spring

  • 其他语言

  • 工具

  • 后端
  • Java
  • 企业应用
轩辕李
2023-06-06
目录

Java JSON处理

# 前言

JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,它以易于阅读和编写的文本格式表示结构化数据。

JSON最初由Douglas Crockford在2001年提出,并且广泛应用于Web应用程序之间的数据交换。

JSON具有以下特点:

  1. 简洁性:JSON使用一种简单的文本格式,易于阅读和编写。
  2. 可读性:JSON的结构对于开发人员来说非常直观和易于理解。
  3. 跨平台和语言支持:由于JSON是一种通用的数据交换格式,它可以在不同平台和编程语言之间进行交换和解析。
  4. 平台无关性:JSON与特定的编程语言无关,因此可以在多种编程语言中使用。
  5. 扩展性:JSON支持嵌套和复杂的数据结构,可以表示更大规模的数据。
  6. Web应用程序支持:由于JSON与JavaScript密切相关,它在Web应用程序中广泛使用,特别是在与服务器进行数据交换时。

JSON在许多应用程序中被广泛使用,特别是在Web开发、API设计和移动应用程序中。它是一种简单、灵活和通用的数据交换格式,被广泛接受并得到支持。

JSON采用键值对的方式组织数据,使用简单的数据类型包括字符串、数字、布尔值、数组、对象和null。这使得JSON非常适合于表示复杂的数据结构。

比如下面的JSON对象,它包含一个名为John的人的信息,包括姓名、年龄、城市、爱好、是否是学生和地址等信息。

{
  "name": "John",
  "age": 30,
  "city": "New York",
  "hobbies": ["reading", "coding", "traveling"],
  "isStudent": false,
  "address": {
    "street": "123 Main St",
    "zip": "10001"
  }
}

# Java中的JSON应用场景

在Java中,JSON的使用场景非常广泛。

下面是一些常见的Java中使用JSON的场景:

  1. 数据交换:JSON作为一种通用的数据交换格式,可以在不同系统、不同编程语言之间进行数据交换。在Java中,可以使用JSON来序列化Java对象为JSON格式,并将其发送给其他系统或服务。反之,也可以将收到的JSON数据反序列化为Java对象。
  2. Web服务:在Web开发中,JSON经常用于在客户端和服务器之间传输数据。Java中的Web服务框架(如Spring MVC、JAX-RS)通常支持将Java对象转换为JSON格式的响应,以及将接收到的JSON数据转换为Java对象。
  3. RESTful API:基于RESTful架构的API通常使用JSON作为数据交换格式。Java中的RESTful框架(如Spring Boot、JAX-RS)可以方便地将Java对象转换为JSON格式,并与客户端进行交互。
  4. 数据存储:有时候需要将Java对象持久化到数据库或文件系统中。在这种情况下,可以将Java对象转换为JSON格式,并将其存储为文本文件或将其作为字段保存在数据库表中。
  5. 配置文件:JSON也可以用作配置文件的格式。许多Java应用程序使用JSON格式来定义和配置应用程序的各种设置和选项。
  6. 测试数据:在编写单元测试或集成测试时,可以使用JSON格式的数据来模拟外部服务的响应或输入数据。这样可以更灵活地定义测试数据,并方便地与Java对象进行序列化和反序列化。

# Java中的JSON库概述

在Java中,有许多第三方库可以用于处理JSON,最流行的库主要是Gson、Jackson、Fastjson等。

Spring MVC的默认JSON库使用了Jackson,Jackson主要有以下优势:

  1. 性能优势:Jackson具有优秀的性能,尤其是在解析和生成JSON时。它的性能要好于其他类似的Java JSON解析库,如Gson和Json-smart。
  2. 灵活性和可扩展性:Jackson提供了一个丰富的特性集,可以很容易地解析,生成和修改JSON。它支持自定义序列化和反序列化,以及自定义的数据类型映射和处理器。
  3. 多种格式支持:虽然Jackson主要用于处理JSON,但它还支持其他数据格式,如XML,YAML,CSV和Smile。这使得Jackson能够方便地处理不同格式的数据,而无需更改API。
  4. 注解支持:Jackson支持多种注解,这使得开发人员可以在数据类中直接定义JSON序列化和反序列化规则。这提高了代码的可读性和可维护性。
  5. 完整的文档和社区支持:Jackson库的文档齐全,使用广泛,有活跃的社区和众多的实用教程,这使得开发人员可以迅速学习和解决问题。
  6. 模块化:Jackson具有模块化的设计,这使得开发人员可以轻松地集成其它功能和扩展库,满足特定的应用需求。

本篇文章主要介绍Jackson的使用,但在这之前,对于Gson和Fastjson做一个简单的介绍。

# Gson

Gson是Google开发的一个用于序列化和反序列化Java对象的库。基于Apache许可面向公众,广泛应用在数据存储、网络传输等方面。Gson提供了一种简单的方式来将Java对象转换为相应的JSON格式字符串,并且可以将JSON字符串解码反序列化成Java对象。

关于Gson的使用,参考 官方教程 (opens new window)

# Fastjson

Fastjson是阿里巴巴开发的一个基于Java语言实现的高性能、功能强大的JSON库。它主要用于将Java对象进行序列化和反序列化。Fastjson 的主要设计目标是快速、安全、清晰,使开发者能够迅速掌握并从容应对大量的 JSON 数据的传输、解析和存储等操作。

关于Fastjson的使用,参考 官方教程 (opens new window)

# Fastjson2

Dubbo 3.2开始,Dubbo默认使用Fastjson2作为JSON序列化和反序列化的实现。Fastjson2是Fastjson的升级版,它在Fastjson的基础上进行了重构,提供了更好的性能和更丰富的功能。

Fastjson2最大的升级是其性能和安全性,关于和其他库的性能对比参考:fastjson2 benchmark (opens new window)

关于Fastjson2的使用,参考 官方教程 (opens new window)

# Jackson的使用

# 1. 添加Jackson依赖

首先,我们需要将Jackson库添加到项目的依赖中。可以在项目的构建文件中添加以下依赖:

<!-- Maven 依赖 -->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.15.2</version>
</dependency>

# 2. 创建ObjectMapper对象

在使用Jackson之前,我们需要创建一个ObjectMapper对象。ObjectMapper是Jackson库的核心类,用于执行JSON的读取、写入和转换操作。可以通过以下方式创建ObjectMapper对象:

import com.fasterxml.jackson.databind.ObjectMapper;

ObjectMapper objectMapper = new ObjectMapper();

需要注意的是,ObjectMapper是一个重对象,因此在应用程序中应该尽量创建一个共享的ObjectMapper对象,避免创建多个ObjectMapper对象。

ObjectMapper是线程安全的,因此可以在多线程环境下共享ObjectMapper对象。

# 3. JSON与Java对象的相互转换

Jackson提供了将JSON数据与Java对象相互转换的功能。

先定义一个Java类:

    record MyClass(String name, Integer age) {}
  1. JSON字符串转Java对象

要将JSON字符串转换为Java对象,可以使用ObjectMapper的readValue()方法。以下是一个示例:

String jsonString = "{\"name\":\"John\",\"age\":30}";

// JSON字符串转Java对象
MyClass myObject = objectMapper.readValue(jsonString, MyClass.class);

在上面的示例中,我们将JSON字符串转换为MyClass对象。

  1. JSON文件转Java对象

要将JSON文件转换为Java对象,可以使用ObjectMapper的readValue()方法,并将文件作为输入源。以下是一个示例:

File jsonFile = new File("data.json");

// JSON文件转Java对象
MyClass myObject = objectMapper.readValue(jsonFile, MyClass.class);

在上面的示例中,我们将JSON文件转换为MyClass对象。

  1. Java对象转JSON字符串

要将Java对象转换为JSON字符串,可以使用ObjectMapper的writeValueAsString()方法。以下是一个示例:

MyClass myObject = new MyClass("John", 30);

// Java对象转JSON字符串
String jsonString = objectMapper.writeValueAsString(myObject);

在上面的示例中,我们将MyClass对象转换为JSON字符串。

  1. Java对象输出到JSON文件

要将Java对象输出到JSON文件,可以使用ObjectMapper的writeValue()方法,并将文件作为输出目标。以下是一个示例:

MyClass myObject = new MyClass("John", 30);
File jsonFile = new File("output.json");

// Java对象输出到JSON文件
objectMapper.writeValue(jsonFile, myObject);

在上面的示例中,我们将MyClass对象输出到名为"output.json"的文件中。

# 4. JSON与Java集合的相互转换

除了转换单个Java对象,Jackson还提供了将JSON与Java集合相互转换的功能。

  1. JSON字符串转Java集合

要将JSON字符串转换为Java集合,可以使用ObjectMapper的readValue()方法,并指定目标集合类型。以下是一个示例:

String jsonArray = "[{\"name\":\"John\",\"age\":30},{\"name\":\"Alice\",\"age\":25}]";

// JSON字符串转Java集合
MyClass[] myObjects = objectMapper.readValue(jsonArray, MyClass[].class);
// 或者
List<MyClass> myObjects = objectMapper.readValue(jsonArray, new TypeReference<List<MyClass>>() {});

在上面的示例中,我们将JSON数组转换为List<MyClass>集合。

上面代码中我们看到了TypeReference,它是Jackson库中的一个类,用于解决Java泛型类型在序列化和反序列化过程中的类型擦除问题。由于Java的泛型信息在运行时被擦除,无法直接获得泛型类型,因此Jackson提供了TypeReference类来捕获泛型类型的信息。

在使用Jackson进行反序列化时,如果目标类型是一个泛型类型,可以使用TypeReference来指定泛型类型,并获取准确的类型信息,以便正确地进行反序列化。

通过使用TypeReference,我们能够解决Java泛型类型的类型擦除问题,确保在反序列化时获得准确的类型信息,从而正确地转换为目标类型。

你也可以把JSON对象转换为Map对象,例如:

List<Map<String,Object> myObjects = objectMapper.readValue(jsonArray, new TypeReference<List<Map<String,Object>>() {});
  1. JSON文件转Java集合

要将JSON文件转换为Java集合,可以使用ObjectMapper的readValue()方法,并将文件作为输入源。以下是一个示例:

File jsonFile = new File("data.json");

// JSON文件转Java集合
List<MyClass> myObjects = objectMapper.readValue(jsonFile, new TypeReference<List<MyClass>>() {});

在上面的示例中,我们将JSON文件转换为List<MyClass>集合。

  1. Java集合转JSON字符串

要将Java集合转换为JSON字符串,可以使用ObjectMapper的writeValueAsString()方法。 以下是一个示例:

List<MyClass> myObjects = new ArrayList<>();
myObjects.add(new MyClass("John", 30));
myObjects.add(new MyClass("Alice", 25));

// Java集合转JSON字符串
String jsonArray = objectMapper.writeValueAsString(myObjects);

在上面的示例中,我们将List<MyClass>集合转换为JSON字符串。

# 5. 复杂对象的处理

演示一下复杂JSON数据的处理,包括嵌套对象、数组等。

假设有如下类:

public class CompositeClass {
    private String name;
    private int age;
    private Address address;
    private List<Pet> pets;

    // 构造函数、Getter和Setter方法等

    public static class Address {
        private String street;
        private String city;

        // 构造函数、Getter和Setter方法等

        // 省略其他代码
    }

    public static class Pet {
        private String name;
        private String species;

        // 构造函数、Getter和Setter方法等

        // 省略其他代码
    }

    // 省略其他代码
}

要把JSON转换为CompositeClass对象,可以使用ObjectMapper的readValue()方法,并指定目标类型。以下是一个示例:

    String jsonString = "{\"name\":\"John\",\"age\":30,\"address\":{\"street\":\"123 Main St\",\"city\":\"New York\"},\"pets\":[{\"name\":\"Fluffy\",\"species\":\"cat\"},{\"name\":\"Buddy\",\"species\":\"dog\"}]}";

    // 将JSON转换为Java对象
    MyClass myObject = objectMapper.readValue(jsonString, MyClass.class);

    // 访问Java对象中的数据
    String name = myObject.getName();
    int age = myObject.getAge();
    String street = myObject.getAddress().getStreet();
    String city = myObject.getAddress().getCity();
    List<Pet> pets = myObject.getPets();

    System.out.println("Name: " + name);
    System.out.println("Age: " + age);
    System.out.println("Street: " + street);
    System.out.println("City: " + city);
    System.out.println("Pets:");

    for (Pet pet : pets) {
        String petName = pet.getName();
        String species = pet.getSpecies();
        System.out.println("  Pet Name: " + petName);
        System.out.println("  Species: " + species);
    }

# 6. JsonNode的用法

ObjectMapper的readTree()方法接受一个JSON字符串作为参数,并返回一个JsonNode对象,表示解析后的JSON数据的树形结构。

以下是一个示例:

    String jsonString = "{\"name\":\"John\",\"age\":30,\"address\":{\"street\":\"123 Main St\",\"city\":\"New York\"},\"pets\":[{\"name\":\"Fluffy\",\"species\":\"cat\"},{\"name\":\"Buddy\",\"species\":\"dog\"}]}";
    // 将JSON字符串转换为JsonNode对象
    JsonNode jsonNode = objectMapper.readTree(jsonString);

    // 访问JsonNode中的数据
    String name = jsonNode.get("name").asText();
    int age = jsonNode.get("age").asInt();
    String street = jsonNode.get("address").get("street").asText();
    String city = jsonNode.get("address").get("city").asText();
    JsonNode pets = jsonNode.get("pets");

    System.out.println("Name: " + name);
    System.out.println("Age: " + age);
    System.out.println("Street: " + street);
    System.out.println("City: " + city);
    System.out.println("Pets:");

    for (JsonNode pet : pets) {
        String petName = pet.get("name").asText();
        String species = pet.get("species").asText();
        System.out.println("  Pet Name: " + petName);
        System.out.println("  Species: " + species);
    }

# 7. 美化JSON输出

如果需要对生成的JSON进行格式化和美化,可以使用ObjectMapper的writerWithDefaultPrettyPrinter()方法。以下是一个示例:

MyClass myObject = new MyClass("John", 30);

// Java对象转JSON字符串(格式化输出)
String jsonString = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(myObject);

# 8. JSON处理的行为和特性配置

ObjectMapper.configure()是Jackson库中ObjectMapper类的一个方法,用于配置ObjectMapper的各种行为和特性。通过该方法,可以自定义和控制ObjectMapper在序列化和反序列化过程中的行为。

configure()方法接受两个参数:一个枚举类型的配置选项,和一个布尔值参数,用于指定配置选项的设置值。下面是一些常用的配置选项及其说明:

  1. DeserializationFeature:用于配置反序列化时的行为选项,例如处理未知属性、忽略空值、支持使用单引号等。

    • FAIL_ON_UNKNOWN_PROPERTIES: 控制是否在遇到未知属性时抛出异常。设置为false表示忽略未知属性,默认为true。
    • ACCEPT_EMPTY_STRING_AS_NULL_OBJECT: 指定是否将空字符串解析为null对象。默认为false。
    • UNWRAP_SINGLE_VALUE_ARRAYS: 控制是否自动将包含单个值的数组解包。默认为false。
    • USE_BIG_DECIMAL_FOR_FLOATS: 控制是否使用BigDecimal来表示浮点数。默认为false,使用double表示浮点数。
    • USE_BIG_INTEGER_FOR_INTS: 控制是否使用BigInteger来表示整数。默认为false,使用int或long表示整数。
  2. SerializationFeature:用于配置序列化时的行为选项,例如处理空值、缩进输出、使用字面值输出枚举等。

    • INDENT_OUTPUT: 控制是否缩进输出的JSON。默认为false。
    • WRITE_DATES_AS_TIMESTAMPS: 控制是否将日期写入为时间戳。默认为true。
    • WRITE_NULL_MAP_VALUES: 控制是否写入空值字段。默认为true。
    • WRITE_ENUMS_USING_TO_STRING: 控制是否使用toString()方法来序列化枚举值。默认为false,使用枚举常量名称。
    • WRITE_CHAR_ARRAYS_AS_JSON_ARRAYS: 控制是否将字符数组写入为JSON数组。默认为false。
  3. JsonParser.Feature:用于配置JSON解析器的行为选项,例如支持单引号、允许注释等。

    • ALLOW_COMMENTS: 控制是否允许在JSON中包含注释。默认为false。
    • ALLOW_SINGLE_QUOTES: 控制是否允许使用单引号作为字符串的引号。默认为false。
    • ALLOW_UNQUOTED_FIELD_NAMES: 控制是否允许在JSON中使用非引号括起的字段名称。默认为false。
  4. JsonGenerator.Feature:用于配置JSON生成器的行为选项,例如缩进输出、使用双引号包装属性等。

    • QUOTE_FIELD_NAMES: 控制是否在生成的JSON中将字段名称用引号括起。默认为true。
    • WRITE_BIGDECIMAL_AS_PLAIN: 控制是否将BigDecimal写入为普通数字,而不是科学计数法。默认为false。

通过调用ObjectMapper.configure()方法,可以根据具体需求设置上述选项及其他选项。以下是一个示例代码,展示了如何使用configure()方法设置ObjectMapper的配置选项:

    ObjectMapper OBJECT_MAPPER = new ObjectMapper();

    // 反序列化时,当遇到未知的属性(在JSON中存在但在目标Java类中不存在)时,控制是否抛出异常。通过将其设置为false,可以忽略未知的属性而不抛出异常。
    OBJECT_MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    // 序列化时,控制是否将字符数组写入为JSON数组。默认情况下,字符数组被序列化为字符串,但将其设置为true可以将字符数组序列化为JSON数组。
    OBJECT_MAPPER.configure(SerializationFeature.WRITE_CHAR_ARRAYS_AS_JSON_ARRAYS, true);
    // 解析时,控制是否允许在JSON中使用单引号作为字符串的引号。将其设置为true可以允许使用单引号来表示字符串,而不仅限于双引号。
    OBJECT_MAPPER.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);

# 9. 时间处理

Jackson默认将Date序列化为时间戳格式(从1970年1月1日UTC开始的毫秒数)。

    SimpleDateFormat df = new SimpleDateFormat("dd-MM-yyyy hh:mm");
    df.setTimeZone(TimeZone.getTimeZone("UTC"));

    Date date = df.parse("01-01-1970 01:00");
    Event event = new Event("party", date);

    ObjectMapper mapper = new ObjectMapper();
    mapper.writeValueAsString(event);

实际输出的结果如下:

{
    "name": "party",
    "eventDate": 3600000
}

# 配置ObjectMapper日期格式

配置ObjectMapper以允许我们设置日期的格式:

    SimpleDateFormat df = new SimpleDateFormat("dd-MM-yyyy hh:mm");

    String toParse = "20-12-2014 02:30";
    Date date = df.parse(toParse);
    Event event = new Event("party", date);

    ObjectMapper mapper = new ObjectMapper();
    mapper.setDateFormat(df);

    String result = mapper.writeValueAsString(event);
    // result = toParse

请注意,我们使用了全局配置,影响整个ObjectMapper的行为。

# 使用@JsonFormat格式化日期

可以使用@JsonFormat注解在单个类上控制日期格式,而不是全局配置整个应用程序的日期格式:

public class Event {
    public String name;

    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd-MM-yyyy hh:mm:ss")
    public Date eventDate;
}

现在让我们进行测试:

@Test
public void whenUsingJsonFormatAnnotationToFormatDate_thenCorrect()
throws JsonProcessingException, ParseException {

    SimpleDateFormat df = new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");
    df.setTimeZone(TimeZone.getTimeZone("UTC"));

    String toParse = "20-12-2014 02:30:00";
    Date date = df.parse(toParse);
    Event event = new Event("party", date);

    ObjectMapper mapper = new ObjectMapper();
    String result = mapper.writeValueAsString(event);
    assertThat(result, containsString(toParse));
}

# 自定义日期序列化器

为了完全控制输出结果,我们可以使用自定义的日期序列化器:

public class CustomDateSerializer extends StdSerializer<Date> {

    private SimpleDateFormat formatter = new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");

    public CustomDateSerializer() {
        this(null);
    }

    public CustomDateSerializer(Class<Date> t) {
        super(t);
    }
    
    @Override
    public void serialize(Date value, JsonGenerator gen, SerializerProvider provider)
      throws IOException, JsonProcessingException {
        gen.writeString(formatter.format(value));
    }
}

现在,我们将它作为"eventDate"字段的序列化器:

public class Event {
    public String name;

    @JsonSerialize(using = CustomDateSerializer.class)
    public Date eventDate;
}

最后,让我们进行测试:

@Test
public void whenUsingCustomDateSerializer_thenCorrect()
throws JsonProcessingException, ParseException {

    SimpleDateFormat df = new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");

    String toParse = "20-12-2014 02:30:00";
    Date date = df.parse(toParse);
    Event event = new Event("party", date);

    ObjectMapper mapper = new ObjectMapper();
    String result = mapper.writeValueAsString(event);
    assertThat(result, containsString(toParse));
}

# 使用Jackson序列化Joda-Time

日期并不总是java.util.Date的实例。实际上,越来越多的情况下,日期是由其他类表示的,而常见的一种是Joda-Time库中的DateTime类。

让我们看看如何使用Jackson对DateTime进行序列化。

我们将使用jackson-datatype-joda模块来支持Joda-Time的序列化:

<dependency>
  <groupId>com.fasterxml.jackson.datatype</groupId>
  <artifactId>jackson-datatype-joda</artifactId>
  <version>2.15.2</version>
</dependency>

然后,我们只需要注册JodaModule即可:

@Test
public void whenSerializingJodaTime_thenCorrect()
throws JsonProcessingException {
    DateTime date = new DateTime(2014, 12, 20, 2, 30,
        DateTimeZone.forID("Europe/London"));

    ObjectMapper mapper = new ObjectMapper();
    mapper.registerModule(new JodaModule());
    mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);

    String result = mapper.writeValueAsString(date);
    assertThat(result, containsString("2014-12-20T02:30:00.000Z"));
}

# 使用Jackson序列化Java 8日期

现在让我们看看如何使用Jackson对Java 8日期(例如LocalDateTime)进行序列化。我们可以使用jackson-datatype-jsr310模块:

<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jsr310</artifactId>
    <version>2.15.2</version>
</dependency>

然后,我们只需要注册JavaTimeModule(JSR310Module已被弃用),Jackson将会处理剩下的事情:

@Test
public void whenSerializingJava8Date_thenCorrect()
throws JsonProcessingException {
    LocalDateTime date = LocalDateTime.of(2014, 12, 20, 2, 30);

    ObjectMapper mapper = new ObjectMapper();
    mapper.registerModule(new JavaTimeModule());
    mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);

    String result = mapper.writeValueAsString(date);
    assertThat(result, containsString("2014-12-20T02:30"));
}

# 10. 自定义序列化和反序列化

在时间处理一章中,我们已经做到了自定义序列化处理,现在补充一下自定义Date的反序列化处理:

public class CustomDateDeserializer extends StdDeserializer<Date> {

    private SimpleDateFormat formatter = 
      new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");

    public CustomDateDeserializer() {
        this(null);
    }

    public CustomDateDeserializer(Class<?> vc) {
        super(vc);
    }

    @Override
    public Date deserialize(JsonParser jsonparser, DeserializationContext context)
      throws IOException, JsonProcessingException {
        String date = jsonparser.getText();
        try {
            return formatter.parse(date);
        } catch (ParseException e) {
            throw new RuntimeException(e);
        }
    }
}

接下来,我们将将其用作"eventDate"的反序列化器:

public class Event {
    public String name;

    @JsonDeserialize(using = CustomDateDeserializer.class)
    public Date eventDate;
}

最后,我们将进行测试:

@Test
public void whenDeserializingDateUsingCustomDeserializer_thenCorrect()
throws JsonProcessingException, IOException {

    String json = "{\"name\":\"party\",\"eventDate\":\"20-12-2014 02:30:00\"}";

    SimpleDateFormat df = new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");
    ObjectMapper mapper = new ObjectMapper();

    Event event = mapper.readerFor(Event.class).readValue(json);
    assertEquals("20-12-2014 02:30:00", df.format(event.eventDate));
}

# Jackson的datatype模块

在前面我们已经看到了Jackson本身提供了很多datatype模块,用于提供对不同数据类型的序列化和反序列化支持。它包含了一些特定类型的序列化和反序列化器,使得在处理特定数据类型时更加方便和灵活。

除了日期和时间类型,datatype模块还提供了对其他数据类型的支持,如Guava类型、Java8类型、PCollections类型等。通过使用相应的模块,可以轻松地处理这些特定类型的序列化和反序列化。

要在项目中使用datatype模块,需要将相应的依赖项添加到项目的构建文件(如pom.xml)。具体依赖项的名称和版本号可以根据需要和使用的Jackson版本进行调整。

以下是常用的datatype模块依赖项:

  • jackson-datatype-jsr310:提供对Java 8日期和时间API的支持。
  • jackson-module-parameter-names:提供了对使用新的JDK 8特性的支持的模块,能够访问构造函数和方法参数的名称,以允许省略@JsonProperty。
  • jackson-datatype-jdk8:支持除日期/时间类型以外的JDK 8数据类型,包括Optional。
  • jackson-datatype-joda:提供对Joda-Time库的支持。
  • jackson-datatype-guava:提供对Guava库的支持。
  • jackson-datatype-pcollections:提供对PCollections库的支持。

在将这些依赖项添加到项目中后,可以通过注册相应的模块来启用它们,例如使用ObjectMapper的registerModule方法。

更多datatype,参考 Third-party datatype modules (opens new window)

# 注册自己的Module

在使用Guava的Table类型时,发现并没有对于Table类反序列化的支持。可以自己来添加支持:

    ObjectMapper OBJECT_MAPPER = new ObjectMapper();
    // 添加序列化方案
    OBJECT_MAPPER.registerModule(new GuavaModule());
    // Guava的Table没有反序列化方案,使用自定义方案
    SimpleModule simpleModule = new SimpleModule();
    simpleModule.addDeserializer(Table.class, new TableDeserializer());
    OBJECT_MAPPER.registerModule(simpleModule);
        
    /**
     * Guavua Table的反序列化方案
     */
    public static class TableDeserializer extends JsonDeserializer<Table<?, ?, ?>> {

        @Override
        public Table<?, ?, ?> deserialize(final JsonParser jp, final DeserializationContext ctxt) throws IOException {
            final ImmutableTable.Builder<Object, Object, Object> tableBuilder = ImmutableTable.builder();
            @SuppressWarnings("unchecked") final Map<Object, Map<Object, Object>> rowMap = jp.readValueAs(Map.class);
            for (final Map.Entry<Object, Map<Object, Object>> rowEntry : rowMap.entrySet()) {
                final Object rowKey = rowEntry.getKey();
                for (final Map.Entry<Object, Object> cellEntry : rowEntry.getValue().entrySet()) {
                    final Object colKey = cellEntry.getKey();
                    final Object val = cellEntry.getValue();
                    tableBuilder.put(rowKey, colKey, val);
                }
            }
            return tableBuilder.build();
        }
    }

# 11. Jackson的注解

在时间处理一节,我们已经领略了@JsonFormat注解的使用。

Jackson提供了一系列的注解,用于控制序列化和反序列化过程中的行为。

下面对一些常用的注解进行说明:

  1. @JsonIgnore:

    • 作用: 标记某个字段或方法,在序列化和反序列化过程中忽略该属性。
    • 示例代码:
    public class Person {
        private String name;
        @JsonIgnore
        private int age;
    
        // 省略构造函数和其他方法
    
        // getters and setters
    }
    
  2. @JsonProperty:

    • 作用: 指定字段或方法在序列化和反序列化中的名称。
    • 示例代码:
    public class Person {
        @JsonProperty("full_name")
        private String name;
    
        // 省略构造函数和其他方法
    
        // getters and setters
    }
    
  3. @JsonAnyGetter:

    • 作用: 在序列化时将Java对象的动态属性作为键值对添加到JSON中。
    • 示例代码:
    public class Person {
        private Map<String, Object> properties = new HashMap<>();
    
        public void setProperty(String key, Object value) {
            properties.put(key, value);
        }
    
        @JsonAnyGetter
        public Map<String, Object> getProperties() {
            return properties;
        }
    }
    
  4. @JsonPropertyOrder:

    • 作用: 指定序列化时属性的顺序。
    • 示例代码:
    @JsonPropertyOrder({ "name", "age", "email" })
    public class Person {
        private String name;
        private int age;
        private String email;
    
        // 省略构造函数和其他方法
    
        // getters and setters
    }
    
  5. @JsonRawValue:

    • 作用: 将字符串属性的原始值直接输出,而不进行额外的转义或引号添加。
    • 示例代码:
    public class Person {
        private String description;
    
        @JsonRawValue
        public String getDescription() {
            return description;
        }
    
        // 省略构造函数和其他方法
    
        // setters
    }
    
  6. @JsonAlias:

    • 作用: 定义一个或多个替代名称,用于在反序列化过程中匹配JSON属性。
    • 示例代码:
    public class Person {
        @JsonAlias({ "firstName", "givenName" })
        private String name;
    
        // 省略构造函数和其他方法
    
        // getters and setters
    }
    
  7. @JsonIgnoreType:

    • 作用: 在序列化和反序列化过程中忽略指定的类型。
    • 示例代码:
    @JsonIgnoreType
    public class SecretDetails {
        private String password;
        private String secretKey;
    
        // 省略构造函数和其他方法
    
        // getters and setters
    }
    
    public class Person {
        private String name;
        private int age;
        private SecretDetails secretDetails;
    
        // 省略构造函数和其他方法
    
        // getters and setters
    }
    

Jackson还提供了很多其他注解,请参考 Jackson Annotations (opens new window) 和 Jackson Annotation Examples (opens new window)

# 12. 对其他数据格式的支持

除了默认的JSON格式外,Jackson提供了很多扩展模块。这些扩展模块提供了对各种数据格式的支持,如XML、YAML、CSV等。

Jackson的Data format modules提供了以下功能:

  1. XML支持(Jackson XML Module): Jackson XML Module允许你将Java对象序列化为XML格式,或将XML反序列化为Java对象。它提供了与Jackson JSON库相似的API和注解,用于控制XML序列化和反序列化的行为。

  2. YAML支持(Jackson YAML Module): Jackson YAML Module允许你将Java对象序列化为YAML格式,或将YAML反序列化为Java对象。它提供了简单而直观的API和注解,用于处理YAML数据。

  3. CSV支持(Jackson CSV Module): Jackson CSV Module提供了对CSV(逗号分隔值)格式的支持。它允许你将Java对象序列化为CSV格式,或将CSV反序列化为Java对象。你可以通过注解或配置来控制CSV的映射规则。

  4. Protobuf支持(Jackson Protobuf Module): Jackson Protobuf Module提供了对Google Protobuf格式的支持。它允许你将Protobuf消息序列化为JSON或XML,或将JSON或XML反序列化为Protobuf消息。

  5. CBOR支持(Jackson CBOR Module): Jackson CBOR Module提供了对CBOR(Concise Binary Object Representation)格式的支持。它允许你将Java对象序列化为CBOR格式,或将CBOR反序列化为Java对象。CBOR是一种紧凑的二进制格式,适用于高效的数据传输和存储。

除了上述提到的模块,Jackson还提供了其他数据格式的支持扩展模块,如Smile(二进制JSON格式)和Properties文件格式。

使用这些Data format modules,你可以在Jackson中处理不同的数据格式,实现灵活的数据转换和交互。你可以根据需要选择相应的模块,并根据模块的文档和示例进行配置和使用。这些模块的引入通常需要添加相应的依赖项到项目的构建文件中。

以上的支持,你都可以在 Data format modules (opens new window) 中找到。

# 13. 其他

Jackson还提供了以下功能:

  • 对于JAX-RS的支持 (opens new window)
  • JSON Views (opens new window)

# 总结

本文介绍了Java中处理JSON的相关知识和常用库,重点介绍了Jackson库的使用。

通过本文的学习,你将能够在Java项目中灵活处理JSON数据,使用Jackson库实现高效的JSON序列化和反序列化。无论是简单的对象转换还是复杂的数据处理,Jackson都提供了丰富的功能和灵活的配置选项,满足各种需求。

希望本文对你理解和使用Java中的JSON处理有所帮助,为你的开发工作提供指导和参考。

祝你变得更强!

编辑 (opens new window)
#Jackson使用教程#JSON
上次更新: 2023/06/14
Java日志系统
Java XML处理

← Java日志系统 Java XML处理→

最近更新
01
Spring Boot版本新特性
09-15
02
Spring框架版本新特性
09-01
03
Spring Boot开发初体验
08-15
更多文章>
Theme by Vdoing | Copyright © 2018-2025 京ICP备2021021832号-2 | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式