最近在处理接口请求进行数据写入的一个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和内置参数确定处理逻辑,扩展性和易用性都很强,在数据同步的场景里挺值得一用的。