【工作中问题解决实践 九】使用@JsonTypeInfo实现请求数据对象多态

文章讲述了如何在SpringMVC项目中使用Jackson的JsonTypeInfo和JsonSubTypes注解,实现在一个接口中根据不同模型对象的类型自动解析和写入数据,提高接口复用性和扩展性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近在处理接口请求进行数据写入的一个case时,我希望上游只使用我一个写入接口去实现不同类型的数据写入,而上游的数据写入Model是各不相同的,这就要求我接口的一个对象可以应对上游不同类型对象的写入请求。关于Jackson的概念不再赘述,参照这篇Blog:【Spring MVC学习笔记 五】SpringMVC框架整合Jackson工具

模拟代码实现示例

为了代码保密,同样用示例的方式进行介绍

基类及子类

接收请求的基类

package com.example.springboot.controller.model;

import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import lombok.Data;

import java.time.LocalDateTime;

/**
 * @author tianmaolin004
 * @date 2023/8/14
 */
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "sceneCode", visible = true)
@JsonSubTypes(value = {
        @JsonSubTypes.Type(value = JmBrandEnterSceneModel.class, name = "JmBrandEnterSceneModel"),
        @JsonSubTypes.Type(value = JmsEnterSceneModel.class, name = "JmsEnterSceneModel")
})
@Data
public class SceneModel {
    /**
     * 场景名称
     */
    private String sceneCode;
    /**
     * 场景时间
     */
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private LocalDateTime sceneTime;
    /**
     * 请求数据来源
     */
    private String requestSource;
}

继承基类的类型一

package com.example.springboot.controller.model;

import lombok.*;

/**
 * @author tianmaolin004
 * @date 2023/8/14
 */
@Data
@EqualsAndHashCode(callSuper = true)
public class JmBrandEnterSceneModel extends SceneModel {
    /**
     * 加盟品牌名称
     */
    private String brandName;
    /**
     * 加盟品牌ID
     */
    private String brandId;
    /**
     * 加盟公司税号
     */
    private String comTaxNo;

}

继承基类的类型二

package com.example.springboot.controller.model;

import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.*;

import java.time.LocalDateTime;

/**
 * @author tianmaolin004
 * @date 2023/8/14
 */
@Data
@EqualsAndHashCode(callSuper = true)
public class JmsEnterSceneModel extends SceneModel {
 
    /**
     * 加盟商名称
     */
    private String franchiserName;
}

数据请求接口

请求接口

package com.example.springboot.controller;

import com.example.springboot.controller.model.SceneModel;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author tianmaolin004
 * @date 2023/8/14
 */
@RestController
@RequestMapping("/scene")
public class SceneSyncController {

    @PostMapping("/sync")
    public void sceneSync(@RequestBody SceneModel sceneModel) {
        System.out.println(sceneModel);
    }
}

请求实验示例

POSTMAN请求一

在这里插入图片描述

请求结果一

在这里插入图片描述

POSTMAN请求二
在这里插入图片描述
请求结果二

在这里插入图片描述

这样随着我请求的不同,Jackson依据不同请求Model中的属性进行解析判断。需要注意的是Jackson本身支持这一注解,并非只有SpringBoot支持,只不过SpringBoot的mvc请求使用了Jackson

JsonTypeInfo用法

这里简单普及一下,@JsonTypeInfo 是 Jackson 库中的注解之一,用于在序列化和反序列化 JSON 数据时处理多态性,特别是在处理继承结构时非常有用。它允许在 JSON 数据中包含有关对象类型的信息,以便正确地进行数据绑定。

以下是 @JsonTypeInfo 注解的主要参数和功能:

  • use:定义类型信息的使用方式。可以使用以下常量之一,以告诉 Jackson 库在序列化和反序列化时如何处理类型信息:

    • JsonTypeInfo.Id.CLASS:将类的全名作为类型信息。
    • JsonTypeInfo.Id.NAME:将一个字符串作为类型信息,需要与 @JsonSubTypes 注解一起使用,一般使用这个配置
    • JsonTypeInfo.Id.MINIMAL_CLASS:类似于 CLASS,但只使用类名的相对路径。
    • JsonTypeInfo.Id.NONE:不包含任何类型信息。
    • JsonTypeInfo.Id.CUSTOM:使用自定义的类型解析器处理类型信息。
  • include:定义类型信息的包含位置。可以是以下常量之一:

    • JsonTypeInfo.As.WRAPPER_OBJECT:将类型信息包装在 JSON 对象中。
    • JsonTypeInfo.As.PROPERTY:将类型信息作为 JSON 属性添加到数据中,一般使用这个配置
  • property:定义用于存储类型信息的属性名。默认为 "@class"。当 include 设置为 JsonTypeInfo.As.PROPERTY 时,此属性用于存储类型信息。

  • visible:设置类型信息是否可见。默认为 false,意味着类型信息不会序列化到 JSON 数据中。如果设置为 true,类型信息将包含在 JSON 数据中,一般使用这个配置设置为true

  • defaultImpl:定义默认的实现类,用于在反序列化时处理无法匹配类型信息的情况。

  • useDefaultImpl:设置是否在无法匹配类型信息时使用默认实现类。默认为 true

综上所述,@JsonTypeInfo 注解用于在序列化和反序列化 JSON 数据时处理多态性和继承结构,通过在数据中包含类型信息来确保正确的映射。

总结一下

JsonTypeInfo注解的使用可以降低同一类数据处理接口的提供数量,调用方可以只调用一个接口,接口内部可以依据调用方组装的Model和内置参数确定处理逻辑,扩展性和易用性都很强,在数据同步的场景里挺值得一用的。

<think>我们正在讨论Jackson库中的@JsonTypeInfo注解。根据引用内容,这个注解用于处理多态类型,即在序列化和反序列化过程中处理父类与子类之间的关系。它的主要作用是在序列化的JSON中包含类型信息,以便在反序列化时能够正确地还原对象。引用[1]和[2]都提到@JsonTypeInfo注解用于在序列化的JSON中包含类型信息。引用[3]则详细说明了@JsonTypeInfo注解的一些属性,如`use`和`include`,并给出了不同的使用方式(如WRAPPER_OBJECT和WRAPPER_ARRAY)。根据这些引用,我们可以总结出@JsonTypeInfo注解的基本用法:1.`use`属性:指定类型标识符的类型,常用的有`JsonTypeInfo.Id.NAME`(使用逻辑类型名称)和`JsonTypeInfo.Id.CLASS`(使用Java类的全限定名)。2.`include`属性:指定类型信息包含的方式,常用的有:-`JsonTypeInfo.As.PROPERTY`:作为JSON的一个属性(默认方式)。-`JsonTypeInfo.As.WRAPPER_OBJECT`:将类型信息作为一个包装对象。-`JsonTypeInfo.As.WRAPPER_ARRAY`:将类型信息作为一个包装数组。3.`property`属性:当`include`属性设置为`PROPERTY`时,可以指定该属性的名称(例如`@JsonTypeInfo(include=JsonTypeInfo.As.PROPERTY,property="type")`,则JSON中会有一个名为"type"的属性来存储类型信息)。此外,引用[3]还提到需要配合使用`@JsonSubTypes`注解来指定子类。`@JsonSubTypes`用于列出父类可以映射的子类,每个子类通过`@JsonSubTypes.Type`注解指定。下面是一个示例:假设有一个父类`Animal`和两个子类`Dog`和`Cat`。我们可以这样配置:```java@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")})publicclassAnimal{//父类属性}publicclassDogextendsAnimal{//子类特有属性}publicclassCatextendsAnimal{//子类特有属性}```在序列化时,Jackson会为每个对象添加一个`type`属性,值为`"dog"`或`"cat"`。反序列化时,Jackson根据`type`属性值来创建相应的子类对象。引用[4]提到在使用MyBatis查询时可能会遇到多态映射的问题,并指出需要确保类型信息被正确识别。这也提醒我们在实际应用中要注意框架之间的整合。因此,@JsonTypeInfo注解的使用方法和作用可以总结为:-**作用**:在序列化时添加类型信息,以便反序列化时能够正确地将JSON数据映射到具体的子类对象。-**使用方法**:1.在父类(或接口)上添加`@JsonTypeInfo`注解,配置类型信息的包含方式(如作为属性、包装对象等)和属性名。2.使用`@JsonSubTypes`注解列出所有可能的子类,并为每个子类指定一个唯一的名称(或使用类名)。注意:在反序列化时,JSON中必须包含配置的类型信息,否则Jackson无法确定具体的子类。最后,根据引用[3]的说明,我们还可以使用其他包含方式(如WRAPPER_OBJECT),但PROPERTY是最常用的方式。相关问题:1.如何在Jackson中处理多态类型的反序列化?2.@JsonTypeInfo注解的include属性有哪些选项?它们有什么区别?3.在使用@JsonTypeInfo注解时,如何避免在JSON中暴露类路径信息(即不使用CLASS)?4.如果JSON数据中的类型信息字段与现有字段冲突怎么办?5.在SpringBoot应用中如何配置Jackson以支持多态类型处理?</think>### Jackson `@JsonTypeInfo` 注解详解 #### 作用与核心功能 `@JsonTypeInfo` 是 Jackson 库中处理多态类型序列化/反序列化的关键注解,主要解决以下问题: 1. **类型保留** 在序列化父类对象时,自动在 JSON 中添加子类类型标识(如 `"type": "Dog"`),确保反序列化时能还原为正确的子类对象[^1][^2]。 2. **多态支持** 使 Jackson 能正确处理继承结构:`Animal` → `Dog`/`Cat` 或 `Shape` → `Circle`/`Rectangle`[^3]。 3. **跨系统兼容** 当 JSON 数据在不同系统间传输时,保持类型信息的完整性[^2]。 --- #### 使用方法(含代码示例) ##### 基础配置 ```java @JsonTypeInfo( use = JsonTypeInfo.Id.NAME, // 使用逻辑类型名 (推荐) include = JsonTypeInfo.As.PROPERTY, // 将类型信息作为独立属性 property = "type" // JSON 中类型标识的属性名 ) @JsonSubTypes({ @JsonSubTypes.Type(value = Dog.class, name = "dog"), @JsonSubTypes.Type(value = Cat.class, name = "cat") }) public class Animal { /* 父类 */ } public class Dog extends Animal { /* 子类1 */ } public class Cat extends Animal { /* 子类2 */ } ``` ##### 序列化/反序列化示例 **序列化结果** ```json { "type": "dog", // 自动添加的类型标识 "name": "Buddy", "breed": "Golden Retriever" } ``` **反序列化代码** ```java ObjectMapper mapper = new ObjectMapper(); Animal animal = mapper.readValue(json, Animal.class); // 自动识别 type 字段并实例化为 Dog 对象 ``` --- #### 关键参数说明 | 参数 | 可选值 | 作用 | |---------------|--------------------------------|----------------------------------------------------------------------| | `use` | `Id.NAME`(推荐) | 使用逻辑名称标识类型(如 `"dog"`) | | | `Id.CLASS` | 使用全限定类名(如 `"com.example.Dog"`,可能暴露敏感信息) | | `include` | `As.PROPERTY`(最常用) | 类型标识作为独立属性(如 `"type": "dog"`) | | | `As.WRAPPER_OBJECT` | 包装为对象:`{"dog": {...}}` | | | `As.WRAPPER_ARRAY` | 包装为数组:`["dog", {...}]` | | `property` | 自定义字符串(如 `"@type"`) | 指定 JSON 中类型标识的属性名 | | `visible` | `true`/`false`(默认 false) | 反序列化后是否保留类型标识字段 | --- #### 实际应用场景 1. **API 返回多态数据** ```java @GetMapping("/animals") public Animal getAnimal() { return new Dog(); // 返回子类对象但声明为父类 } ``` Spring MVC 自动序列化为含 `type` 字段的 JSON[^4]。 2. **数据库多态映射** 配合 JPA/Hibernate,将 `@TypeDiscriminator` 与 `@JsonTypeInfo` 结合,实现从数据库到 JSON 的全链路多态支持[^4]。 3. **消息队列通信** 在 Kafka/RabbitMQ 消息中传递多态对象,确保消费者能正确解析类型。 --- #### 常见问题解决 1. **类型标识冲突** ```java @JsonTypeInfo(property = "@type") // 使用非常用属性名避免冲突 ``` 2. **子类未注册** 确保所有子类都在 `@JsonSubTypes` 中声明,否则抛出 `InvalidTypeIdException`。 3. **PROPERTY 模式字段缺失** 反序列化时若 JSON 缺少类型标识字段,需配置默认类型: ```java @JsonTypeInfo(defaultImpl = DefaultAnimal.class) ``` --- #### 最佳实践建议 1. **优先使用 `Id.NAME`** 避免 `Id.CLASS` 暴露类路径信息,提升安全性和兼容性[^3]。 2. **统一命名规范** 子类名称(如 `name = "dog"`)建议使用小写蛇形命名。 3. **结合 `@JsonSubTypes`** 必须配套使用以声明有效的子类型映射关系[^1][^3]。 4. **测试边界情况** 验证:空值处理、未知类型回退、循环引用等场景。 > 通过合理配置 `@JsonTypeInfo`,可高效解决 Java 多态类型在 JSON 序列化中的类型擦除问题实现类型安全的跨系统数据交换[^1][^2]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

存在morning

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值