You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 05:09:14