有些情况下,服务端提供了一个抽象类及其多个实现类,当前端传递 json 数据到后端时,我们希望映射得到的对象数据是根据某个特征区分开的具体的实现类对象。

实现方式

可以通过 jackson 实现序列化及反序列化,并使用 jackson 包中的
@JsonTypeInfo @JsonSubTypes 这两个注解,去实现该功能

示例

抽象类对象
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY, visible = true, property = "type")
@JsonSubTypes({
        @JsonSubTypes.Type(value = DncCheckBox.class, name = "CHECKBOX"),
        @JsonSubTypes.Type(value = DncDate.class, name = "DATE"),
        @JsonSubTypes.Type(value = DncInputNumber.class, name = "INPUTNUMBER"),
        @JsonSubTypes.Type(value = DncSerialNumber.class, name = "SERIALNUMBER"),
        @JsonSubTypes.Type(value = DncText.class, name = "TEXT")
})
public abstract class DocNoComponent {
    public String type; //类型

    public void setType(String type) {
        this.type = type;
    }
    
    public String getType() {
        return type;
    }
}
若干实现类
public class DncCheckBox extends DocNoComponent {
    public DncCheckBox() {
        super();
    }
}
public class DncDate extends DocNoComponent {
    public DncDate() {
        super();
    }
}
public class DncInputNumber extends DocNoComponent {
    public DncInputNumber() {
        super();
    }
}
测试接口及前端传递请求体
@PostMapping("/test00091")
public void test00091(@RequestBody List<DocNoComponent> configItems){
    System.out.println(configItems);
}
[
    {
        "type": "TEXT"
    },
    {
        "type": "DATE"
    },
    {
        "type": "INPUTNUMBER"
    }
]
接参结果

SpringBoot -- 请求数据多态映射(jackson)-LMLPHP

@JsonTypeInfo 和 @JsonSubTypes

以上的方式我们是通过根据对象中的某个属性,判断反序列化时应该反序列化为那种类型的对象,如果不配置 property 字段,默认读取 json 对象中的 @type 属性
SpringBoot -- 请求数据多态映射(jackson)-LMLPHP

其他常用映射方式
1__ 指定对象类型(两种方式)

use=JsonTypeInfo.Id.CLASS 通过 json 数据中的 @class 属性指定目标对象的全限定类型路径,property 可以不赋值,默认读取 @class 属性。
use=JsonTypeInfo.Id.MINIMAL_CLASS 通过 json 数据中的 @c 属性指定目标对象的全限定类型路径,property 可以不赋值,默认读取 @c属性。

@JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, include=JsonTypeInfo.As.PROPERTY, property="@class")
//@JsonTypeInfo(use=JsonTypeInfo.Id.MINIMAL_CLASS, include=JsonTypeInfo.As.PROPERTY, property="@c")
public abstract class DocNoComponent { 
	...
}
{
  "@class" : "com.***.DncDate",
  "type" : "DATE"
}

SpringBoot -- 请求数据多态映射(jackson)-LMLPHP

2__自定义序列化、反序列化方式

use = JsonTypeInfo.Id.CUSTOM 配合 @JsonTypeIdResolver 使用
自定义解析器里面,对于反序列化,我们主要关注实现 typeFromId 这个方法,
在这里我们通过接收到的 @no 属性去判断映射为具体对象

@JsonTypeInfo(use = JsonTypeInfo.Id.CUSTOM, include = JsonTypeInfo.As.EXISTING_PROPERTY, visible = true, property = "@no")
@JsonTypeIdResolver(DocNoComponentTypeIdResolver.class)
public abstract class DocNoComponent {
...
}
public class DocNoComponentTypeIdResolver implements TypeIdResolver {
    private JavaType baseType;

    @Override
    public void init(JavaType javaType) {
        baseType = javaType;
    }


    @Override
    public String idFromValue(Object o) {
        return idFromValueAndType(o, o.getClass());
    }


    @Override
    public String idFromValueAndType(Object o, Class<?> aClass) {
        String name = aClass.getName();
        String[] splits = StringUtils.split(name, ".");
        String className = splits[splits.length - 1];
        return className;
    }


    @SneakyThrows
    @Override
    public JavaType typeFromId(DatabindContext databindContext, String type) {
        Class clazz = null;
        switch(type){
            case "1":
                clazz = Class.forName("com.***.DncCheckBox");
                break;
            case "2":
                clazz = Class.forName("com.***.DncDate");
                break;
            case "3":
                clazz = Class.forName("com.***.DncInputNumber");
                break;
            default:
                break;
        }
        if (clazz == null) {
            throw new IllegalStateException("cannot find class '" + type + "'");
        }
        return databindContext.constructSpecializedType(baseType, clazz);
    }

    @Override
    public String idFromBaseType() {
        return idFromValueAndType(null, baseType.getClass());
    }

    @Override
    public String getDescForKnownTypeIds() {
        return null;
    }

    @Override
    public JsonTypeInfo.Id getMechanism() {
        return JsonTypeInfo.Id.CUSTOM;
    }
}
[
    {
        "@no":"2",
        "type": "TEXT"
    }
]

SpringBoot -- 请求数据多态映射(jackson)-LMLPHP

10-31 23:40