schemars
schemars copied to clipboard
Self-referencing a type ends up creating a new definition
For the following Rust enum:
#[derive(JsonSchema, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase", tag = "function")]
pub enum Test {
RandomVariantA(String),
RandomVariantB(i32),
RecursiveTests(Vec<Test>),
}
the following JSON schema is generated:
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Test",
"oneOf": [
{
"type": ["object", "string"],
"required": ["function"],
"properties": {
"function": {
"type": "string",
"enum": ["randomVariantA"]
}
}
},
{
"type": ["object", "integer"],
"format": "int32",
"required": ["function"],
"properties": {
"function": {
"type": "string",
"enum": ["randomVariantB"]
}
}
},
{
"type": ["object", "array"],
"items": {
"$ref": "#/definitions/Test" // <-------- reference to another extraneous `Test` definition
},
"required": ["function"],
"properties": {
"function": {
"type": "string",
"enum": ["recursiveTests"]
}
}
}
],
"definitions": {
"Test": {
"oneOf": [
{
"type": ["object", "string"],
"required": ["function"],
"properties": {
"function": {
"type": "string",
"enum": ["randomVariantA"]
}
}
},
{
"type": ["object", "integer"],
"format": "int32",
"required": ["function"],
"properties": {
"function": {
"type": "string",
"enum": ["randomVariantB"]
}
}
},
{
"type": ["object", "array"],
"items": {
"$ref": "#/definitions/Test"
},
"required": ["function"],
"properties": {
"function": {
"type": "string",
"enum": ["recursiveTests"]
}
}
}
]
}
}
}
The issue here is that instead of using a self-reference for the variant RecursiveTests
, Schemars creates another definition at #/definitions/Test
. What I would expect would be this instead:
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Test",
"oneOf": [
{
"type": ["object", "string"],
"required": ["function"],
"properties": {
"function": {
"type": "string",
"enum": ["randomVariantA"]
}
}
},
{
"type": ["object", "integer"],
"format": "int32",
"required": ["function"],
"properties": {
"function": {
"type": "string",
"enum": ["randomVariantB"]
}
}
},
{
"type": ["object", "array"],
"items": {
"$ref": "#" // <------------------------- self reference
},
"required": ["function"],
"properties": {
"function": {
"type": "string",
"enum": ["recursiveTests"]
}
}
}
]
}
Is there currently a way to achieve this with Schemars or is this a bug?
I have experienced the same issue, but with types that have an indirect reference cycle instead (e.g. A references to B, which references to C, which references to A again), so using self references in the generated schema would not be enough in that case.
Maybe an expected output that also takes into account indirect reference cycles would be the following:
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Test",
"$ref": "#/definitions/Test", // <------------------------- reference to the definition
"definitions": {
"Test": {
"oneOf": [
{
"type": ["object", "string"],
"required": ["function"],
"properties": {
"function": {
"type": "string",
"enum": ["randomVariantA"]
}
}
},
{
"type": ["object", "integer"],
"format": "int32",
"required": ["function"],
"properties": {
"function": {
"type": "string",
"enum": ["randomVariantB"]
}
}
},
{
"type": ["object", "array"],
"items": {
"$ref": "#/definitions/Test"
},
"required": ["function"],
"properties": {
"function": {
"type": "string",
"enum": ["recursiveTests"]
}
}
}
]
}
}
}
Same thing here:
#[derive(JsonSchema)]
struct User {
user_id: i32,
first_name: String,
last_name: String,
role: Role,
family: Vec<User>,
gender: Gender,
}
Produces the following:
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "User",
"type": "object",
"required": [
"family",
"first_name",
"gender",
"last_name",
"role",
"user_id"
],
"properties": {
"family": {
"type": "array",
"items": {
"$ref": "#/definitions/User"
}
},
"first_name": {
"type": "string"
},
"gender": {
"$ref": "#/definitions/Gender"
},
"last_name": {
"type": "string"
},
"role": {
"$ref": "#/definitions/Role"
},
"user_id": {
"type": "integer",
"format": "int32"
}
},
"definitions": {
"Gender": {
"type": "string",
"enum": [
"Male",
"Female",
"Other"
]
},
"Role": {
"type": "string",
"enum": [
"Foo",
"Admin"
]
},
"User": {
"type": "object",
"required": [
"family",
"first_name",
"gender",
"last_name",
"role",
"user_id"
],
"properties": {
"family": {
"type": "array",
"items": {
"$ref": "#/definitions/User"
}
},
"first_name": {
"type": "string"
},
"gender": {
"$ref": "#/definitions/Gender"
},
"last_name": {
"type": "string"
},
"role": {
"$ref": "#/definitions/Role"
},
"user_id": {
"type": "integer",
"format": "int32"
}
}
}
}
}
Has anyone resolved this issue?