json 绑定多子类
假设存在如下需求:
- 自动创建用户指定的数量的多数据类型的数据
- 用户指定的数量应用于所有数据类型
- 数据类型包括:时间、数值和文本等
- 时间类型可指定具体的时间格式(如yyyy-MM-dd,HH:mm:ss yyyy/MM/dd等)、单位(年、月、日、时、分、秒)、最大最小值以及值间隔等内容
- 数值类型可指定最大最小值和值间隔等
- 文本无需指定特殊字段
根据需求可得到如下pojo:
- 时间
// 时间
public class TimeData {
private final String DEFAULT_FORMAT = "yyyy-MM-dd HH:mm:ss";
private final Unit DEFAULT_UNIT = Unit.DAY;
private final Date DEFAULT_MIN = new Date();
private String unit;
private String format;
private Date min;
private Date max;
private Integer step;
}
// 数值
public class IntAmountData {
private final Integer DEFAULT_MIN = 0;
private final Integer DEFAULT_MAX = 100;
private final Integer DEFAULT_STEP = 1;
private Integer min;
private Integer max;
private Integer step;
}
// 文本
public class StringData {
private final String[] names = {"梦琪", "忆柳", "之桃", "慕青", "问兰", "尔岚", "元香",
"初夏", "沛菡", "傲珊", "曼文", "乐菱", "痴珊", "恨玉", "惜文", "香寒", "新柔", "语蓉",
"海安", "夜蓉", "涵柏", "水桃", "醉蓝", "春儿", "语琴", "从彤", "傲晴", "语兰", "又菱"};
关键的地方在于,如何体现出用户指定的数量?
此时有两种解决方案:
- 方案一:在每种数据类型中添加数量字段
// 时间
public class TimeData {
private final String DEFAULT_FORMAT = "yyyy-MM-dd HH:mm:ss";
private final Unit DEFAULT_UNIT = Unit.DAY;
private final Date DEFAULT_MIN = new Date();
private String unit;
private String format;
private Date min;
private Date max;
private Integer step;
// 为时间类添加数量字段
private Integer limit;
}
// 数值
public class IntAmountData {
private final Integer DEFAULT_MIN = 0;
private final Integer DEFAULT_MAX = 100;
private final Integer DEFAULT_STEP = 1;
private Integer min;
private Integer max;
private Integer step;
// 为数值类添加数量字段
private Integer limit;
}
// 文本
public class StringData {
private final String[] names = {"梦琪", "忆柳", "之桃", "慕青", "问兰", "尔岚", "元香",
"初夏", "沛菡", "傲珊", "曼文", "乐菱", "痴珊", "恨玉", "惜文", "香寒", "新柔", "语蓉",
"海安", "夜蓉", "涵柏", "水桃", "醉蓝", "春儿", "语琴", "从彤", "傲晴", "语兰", "又菱"};
// 为文本类添加数量字段
private Integer limit;
}
此方案下的json请求如下:
curl -XPOST '/create' -d '
{
"timeData": {
"unit": "",
"format": "",
"min": "",
"max": "",
"step": 1,
"limit": 10
},
"intAmountData": {
"min": "",
"max": "",
"step": 1,
"limit": 10
},
"stringData": {
"limit": 10
}
}
- 方案二:在父类中定义数量字段,每种子类型通过继承的方式获得数量字段
// 父类
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
@JsonSubTypes(
{
@JsonSubTypes.Type(value = TimeData.class, name = "time"),
@JsonSubTypes.Type(value = StringData.class, name = "string"),
@JsonSubTypes.Type(value = IntAmountData.class, name = "int")
}
)
public class BaseData {
// 用于指定每种子类的类型名
private String type;
// 只在父类中添加数量字段
private Integer limit;
}
// 时间
public class TimeData extends BaseData {
private final String DEFAULT_FORMAT = "yyyy-MM-dd HH:mm:ss";
private final Unit DEFAULT_UNIT = Unit.DAY;
private final Date DEFAULT_MIN = new Date();
private String unit;
private String format;
private Date min;
private Date max;
private Integer step;
}
// 数值
public class IntAmountData extends BaseData {
private final Integer DEFAULT_MIN = 0;
private final Integer DEFAULT_MAX = 100;
private final Integer DEFAULT_STEP = 1;
private Integer min;
private Integer max;
private Integer step;
}
// 文本
public class StringData extends BaseData {
private final String[] names = {"梦琪", "忆柳", "之桃", "慕青", "问兰", "尔岚", "元香",
"初夏", "沛菡", "傲珊", "曼文", "乐菱", "痴珊", "恨玉", "惜文", "香寒", "新柔", "语蓉",
"海安", "夜蓉", "涵柏", "水桃", "醉蓝", "春儿", "语琴", "从彤", "傲晴", "语兰", "又菱"};
此方案下的json请求如下:
// 时间
curl -XPOST '/create' -d '
{
"baseData": {
// 指定子类类型为时间
"type": "time",
"unit": "",
"format": "",
"min": "",
"max": "",
"step": 1,
"limit": 10
}
}
// 数值
curl -XPOST '/create' -d '
{
"baseData": {
// 指定子类类型为数值
"type": "int",
"min": "",
"max": "",
"step": 1,
"limit": 10
}
}
// 文本
curl -XPOST '/create' -d '
{
"baseData": {
// 指定子类类型为文本
"type": "string",
"limit": 10
}
}
通过此方式,可实现多子类的统一,相比方案一更容易拓展和维护。
方案二相关注解说明
JsonTypeInfo
use:指定类型元数据的形式,用于序列化和反序列化具体实例(接受一个JsonTypeInfo.Id的枚举值)
JsonTypeInfo.Id:定义序列化和反序列化中的类型标识
- NONE:不明确指定类型标识类型,需根据上下文判断类型信息
- CLASS:使用全限定名作为类型标识
- MINIMAL_CLASS:采用类名作为类型标识
- NAME:使用逻辑类型名作为类型标识
- CUSTOM:采用自定义的方式定义类型标识
include:指定如何包含类型元数据(接受一个JsonTypeInfo.As的枚举值)
JsonTypeInfo.As:枚举类,包含以下值:
- PROPERTY:以成员变量的形式包含类型元数据
- WRAPPER_OBJECT:以包装类的形式包含类型元数据
- WRAPPER_ARRAY:以成员变量的形式包含类型元数据
- EXTERNAL_PROPERTY:以成员变量的形式包含类型元数据
property:当include的值为JsonTypeInfo.As.PROPERTY时,指定作为类型元数据的变量名
defaultImpl:指定默认实现
visible:用于定义类型标识符值是作为JSON流的一部分传递给反序列化器(true),还是由TypeDeserializer(false)处理和删除。默认false
JsonSubTypes:接受JsonSubTypes.Type数组参数,用于指定多个子类信息
JsonSubTypes.Type:指定具体的子类信息
value:子类全限定名
name:用作类型标识的子类逻辑名