在java中,多态是非常常见的,一个父类与多个子类,json也是常用的数据格式,我们偶尔会遇到需要将json数据根据不同的场景转化为不同的实体的情况,这时可以使用json的@JsonTypeInfo与@JsonSubTypes注解。
SpringBoot后端接口接收json类型的参数时,默认是使用jackson来将json数据转化为对应的实例对象,因为了解了jackson怎么将json映射到不同的类就可以了,在spring boot中的用法也是一样的。
jackson示例代码
java版本:21
jackson版本
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.17.0</version>
</dependency>
基类
在基类上使用@JsonTypeInfo注解
use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY表示使用已存在的某个属性来判断序列化成哪个类
property = "type"表示用type属性来判断序列化成哪个类
defaultImpl = PublicParent.class表示默认的实现是哪个类,当type没有匹配到具体的类时,对应的json都会转为PublicParent
@JsonSubTypes可以注册多个子类,每个子类用@JsonSubTypes.Type声明
@JsonSubTypes.Type(value = SonA.class, name = "a")声明了一个子类SonA,其中name = "a"表示当type字段的值为a时,将json转为SonA,name字段与@JsonTypeInfo中的property的值配合
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import lombok.Getter;
import lombok.Setter;
/**
* 公共父类
*
* @since 2024/3/27
**/
@Setter
@Getter
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "type",
defaultImpl = PublicParent.class)
@JsonSubTypes({
@JsonSubTypes.Type(value = SonA.class, name = "a")
})
public class PublicParent {
private String type;
private String name;
}
子类
子类不需要任何额外修改
import lombok.Getter;
import lombok.Setter;
/**
* 孩子A
*
* @since 2024/3/27
**/
@Setter
@Getter
public class SonA extends PublicParent {
private String a;
}
单元测试
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
class PublicParentTest {
private static final ObjectMapper mapper = new ObjectMapper();
@BeforeAll
static void beforeAll() {
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
}
@Test
void should_return_parent_when_not_son() {
String json = """
{"type": "b", "name": "parent", "a": "i is a"}""";
PublicParent res = Assertions.assertDoesNotThrow(() -> mapper.readValue(json, PublicParent.class));
Assertions.assertEquals("parent", res.getName());
}
@Test
void should_return_son_when_is_son() {
String json = """
{"type": "a", "name": "son", "a": "i is a"}""";
SonA res = (SonA) Assertions.assertDoesNotThrow(() -> mapper.readValue(json, PublicParent.class));
Assertions.assertEquals("i is a", res.getA());
}
}