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