Victools JSON Schema生成器为同一类生成两个定义的原因咨询
问题描述
使用victools提供的JSON Schema生成器(可基于Java类生成对应JSON Schema)时,若给存在公共超类的两个类建立关联,同时使用@JsonTypeName注解,会生成不符合预期的结果。
参考示例代码如下:
Root类定义
@JsonTypeName("Root") public class Root { private String rootName; ... private List<SuperClass1> superclass1 = new ArrayList<SuperClass1>(); ... }
公共超类定义
@JsonTypeName("SuperClass1") @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "type") @JsonSubTypes({ @JsonSubTypes.Type(value = Sub1.class, name = "Sub1"), @JsonSubTypes.Type(value = Sub2.class, name = "Sub2")}) public abstract class SuperClass1 { String name; int someThingElse; Root root; ... }
Sub1子类定义
@JsonTypeName("Sub1") public class Sub1 extends SuperClass1 { String sub1; ... Sub2 sub2; ... }
Sub2子类定义
@JsonTypeName("Sub2") public class Sub2 extends SuperClass1{ String sub2; ... }
上述代码生成的JSON Schema如下:
{ "$schema" : "http://json-schema.org/draft-07/schema#", "definitions" : { "Sub1" : { "type" : "object", "properties" : { "root" : { "$ref" : "#" }, "sub1" : { "type" : "string" }, "sub2" : { "$ref" : "#/definitions/Sub2-2" } } }, "Sub2-1" : { "type" : "object", "properties" : { "root" : { "$ref" : "#" }, "sub2" : { "type" : "string" } } }, "Sub2-2" : { "allOf" : [ { "$ref" : "#/definitions/Sub2-1" }, { "type" : "object", "properties" : { "type" : { "const" : "json_test.Sub2" } }, "required" : [ "type" ] } ] } }, "type" : "object", "properties" : { "rootName" : { "type" : "string" }, "superclass1" : { "type" : "array", "items" : { "anyOf" : [ { "allOf" : [ { "$ref" : "#/definitions/Sub1" }, { "type" : "object", "properties" : { "type" : { "const" : "json_test.Sub1" } }, "required" : [ "type" ] } ] }, { "$ref" : "#/definitions/Sub2-2" } ] } } } }
该现象触发条件:Sub1类的属性引用Sub2类,且超类配置了@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "type")注解。
核心疑问:为何生成的Schema的definitions节点中会出现Sub2-1和Sub2-2两个定义,而非预期的单个Sub2定义?
原因说明
这是victools JSON Schema生成器处理多态类型的默认行为,本质是按引用场景拆分独立定义,避免不同场景的校验规则冲突,具体逻辑:
- 生成器遍历类结构输出Schema时,每遇到一个类型就会生成对应的定义节点。如果同一个类型在不同引用场景下需要匹配不同校验规则,生成器不会修改已经生成、被其他节点引用的定义,只会生成带数字后缀的新定义做区分。
- 生成流程中第一次碰到
Sub2,是在处理Sub1类的Sub2 sub2属性时:这个属性的静态类型就是具体类Sub2,Jackson序列化这个属性时不会自动添加@JsonTypeInfo指定的type鉴别字段,所以生成器只需要输出匹配Sub2字段结构的纯结构定义,也就是Sub2-1,这个定义不要求JSON必须带type字段。 - 第二次碰到
Sub2,是在处理SuperClass1的多态子类型列表时:此时Sub2是作为List<SuperClass1>的集合元素出现,Jackson序列化时会自动给对象写入type字段存类全限定名,对应的Schema必须追加type字段值固定为json_test.Sub2的必填约束。这时候Sub2-1已经被Sub1.sub2属性引用,如果直接给Sub2-1加上type字段的必填规则,会导致Sub1.sub2序列化出来的、不带type字段的合法JSON被Schema判定为不符合规则,因此生成器不会改动已有的Sub2-1,而是新建Sub2-2定义,通过allOf组合的方式,既复用Sub2-1的结构规则,又追加多态场景需要的type字段约束。
Sub1没有出现带数字后缀的拆分定义,只是处理顺序差异导致的:Sub1没有被其他类以静态具体类型的方式提前引用,第一次生成Sub1定义时只输出了纯结构,后续处理多态约束时直接在数组的anyOf引用处临时拼接了type字段规则,没有生成新的命名定义。
内容的提问来源于stack exchange,提问作者user11583193
相关产品推荐
相关产品推荐

