使用Jackson库实现多参数构造函数的JSON反序列化
[LOADING...]
1. 简介
在本教程中,我们将学习如何使用 Jackson 将 JSON 反序列化为使用多参数构造函数的 Java 对象。
默认情况下,Jackson 要求提供一个不带任何参数的默认构造函数。 字段通过 setter 方法或反射进行设置。如果我们希望 Jackson 使用非默认构造函数,则需要使用 @JsonCreator 注解来标注该构造函数。此注解可以应用于记录(Record)和枚举(Enum)的构造函数以及静态工厂方法。在本教程中,我们将探讨所有这些情况。
我们还将了解 Jackson 提供的各种选项,以减少反序列化所需的注解数量。
2. 设置
2.1. Maven 依赖
除了基础的 Jackson 依赖 外,我们还需要 Jackson 的参数名称模块:
2.2. Java 类
在整个教程中,我们将使用 Ticket 类:
请注意,我们仅为 guest 属性定义了 setter 方法,同时提供了所有属性的 getter。这里我们特意省略了其他三个属性的 setter,以演示反序列化的行为。此外,我们在 eventName 属性上使用了 @JsonProperty,以展示 Jackson 提供的序列化字段的不同方式。
以下是 Currency 枚举:
2.3. 待反序列化的 JSON
以下是我们将在示例中反序列化的 JSON:
3. 默认反序列化
我们需要定义一个默认的无参构造函数,因为类中包含一个 final 属性。如果我们只定义一个接受 currency 和 price 作为参数的构造函数,Jackson 将无法反序列化该对象:
Jackson 将 抛出异常:
4. 使用 @JsonCreator 进行反序列化
要指定反序列化时应使用哪个构造函数,我们可以使用 @JsonCreator 注解。
4.1. 定义带有 @JsonCreator 和 @JsonProperty 的构造函数
如果我们想使用双参数构造函数,需要使用 @JsonCreator 标注它,并使用 @JsonProperty 标注参数:
Jackson 将按以下方式反序列化 JSON:
- currency 和 price 这两个属性在构造函数中设置。
- guest 属性通过其 setter 方法设置。
- eventName 属性没有 setter 方法,它是通过反射设置的。
如果属性没有 setter 方法,Jackson 会根据名称通过反射设置该属性。 如果 Java 属性名与 JSON 字段名不同,我们可以使用 @JsonProperty 注解来定义 JSON 字段名。在我们的示例中,我们用 @JsonProperty("event") 标注了 eventName 属性,以表明 JSON 字段名为 event,而 Java 属性名为 eventName。
我们只能用 @JsonCreator 标注一个构造函数。如果我们标注了第二个构造函数:
Jackson 将抛出异常:
4.2. 不带 @JsonProperty 的构造函数
Jackson 使用反射将 JSON 字段名映射到 Java 类属性。这适用于类属性,但不适用于方法参数名。因此,如果我们定义的构造函数没有 @JsonProperty 注解:
我们将得到一个异常:
Java 在运行时不会保留方法参数名称,因此我们需要用 @JsonProperty 标注参数,以指定应映射到每个参数的 JSON 字段名。
如果我们想避免使用 @JsonProperty 标注参数,可以注册 ParameterNamesModule 并将 parameters 标志添加到编译器中。
首先,添加 Maven 依赖:
然后,将 ParameterNamesModule 注册到对象映射器(Object Mapper)中:
并将 parameters 标志添加到编译器配置中:
4.3. 使用 ConstructorDetector 进行配置
我们仍然需要添加 @JsonCreator 注解来标记想要用于反序列化的构造函数。
从 Jackson 2.12 开始,我们可以注册一个 ConstructorDetector 来指定反序列化时应使用的构造函数,而无需使用 @JsonCreator 进行标注:
值得注意的是,如果我们配置了 Jackson 自动检测构造函数,同时又用 @JsonCreator 标注了多个构造函数中的一个,那么被标注的构造函数将优先于其他被检测到的构造函数。
5. Records
Jackson 可以使用默认提供的规范构造函数来反序列化 Java Record。考虑以下 Record:
以及以下 JSON:
Jackson 可以在不需要无参构造函数的情况下将 JSON 反序列化为 Java Record。 如果我们注册了 ParameterNamesModule 并使用 parameters 标志编译代码,则无需使用 @JsonProperty 标注 Record 组件:
在某些情况下,我们可能希望自定义规范构造函数:
同样,我们不需要用 @JsonCreator 标注此构造函数,因为 Jackson 默认会使用规范构造函数。
我们需要使用 @JsonCreator 注解的一种情况是添加静态工厂方法时:
与使用构造函数不同,静态工厂方法可以有额外的参数:
而非规范构造函数即使被标注了 @JsonCreator,也不会被使用:
Jackson 会忽略此构造函数,转而使用规范构造函数。
6. 枚举 (Enums)
@JsonCreator 也可用于反序列化枚举。默认情况下,Jackson 使用枚举的名称来 反序列化枚举。
我们可以通过使用 @JsonFormat 标注枚举来更改默认行为:
枚举值 EUR 将被序列化为以下 JSON:
然而,反序列化会因以下异常而失败:
这是因为 Jackson 尝试基于枚举的名称(即 EUR)进行反序列化。 我们可能认为用 @JsonCreator 标注构造函数能解决问题:
这行不通,因为枚举构造函数是私有的,Jackson 无法使用。 解决方案是定义一个静态工厂方法并用 @JsonCreator 标注它:
7. 结论
在本文中,我们学习了如何使用多参数构造函数将 JSON 反序列化为 Java 对象。我们了解了如何在 Java 类、枚举和 Record 中使用 @JsonCreator。此外,我们还了解到参数名称模块和构造函数检测器设置有助于减少所需的注解数量。
一如既往,本文中的代码可在 GitHub 上获取。