Generating JSON Schema
Basics
After analyzing a type, extracting and modifying data, the JSON schema is created with two main steps:
1. generateJsonSchema
: generates an independent JSON schema for each processed type. At this stage, the schemas may not be 100% valid JSON schemas.
2. compile[...]
: this step takes the independent JSON Schemas created by the generateJsonSchema
-step and merges them into one or multiple final valid schemas
class ExampleClass(
val nested: NestedClass,
val number: Int,
val text: String?,
)
class NestedClass(
val flag: Boolean
)
compileInlining()
takes the independent JSON Schemas generated by generateJsonSchema
and creates a single schema by simply inlining all referenced schemas.
initial<ExampleClass>()
.analyzeTypeUsingReflection()
.generateJsonSchema()
.compileInlining()
.json
{
"type": "object",
"required": [ "nested", "number" ],
"properties": {
"nested": {
"type": "object",
"required": [ "flag" ],
"properties": {
"flag": {
"type": "boolean"
}
}
},
"number": {
"type": "integer",
"minimum": -2147483648,
"maximum": 2147483647
},
"text": {
"type": "string"
}
}
}
compileReferencing()
takes the independent JSON Schemas generated by generateJsonSchema
and only references them. Very simple schemas like primitives are still inlined.
Referenced schemas are returned in an extra property .compileReferencing().definitions
.
initial<ExampleClass>()
.analyzeTypeUsingReflection()
.generateJsonSchema()
.compileReferencing()
.json
{
"type": "object",
"required": [ "nested", "number" ],
"properties": {
"nested": {
"$ref": "#/definitions/examples.NestedClass"
},
"number": {
"type": "integer",
"minimum": -2147483648,
"maximum": 2147483647
},
"text": {
"type": "string"
}
}
}
.compileReferencing().definitions["examples.NestedClass"]
):
{
"type": "object",
"required": ["flag"],
"properties": {
"flag": {
"type": "boolean"
}
}
}
compileReferencingRoot()
behaves the same as compileReferencing()
, but also references the root schema.
Referenced schemas are returned in an extra property .compileReferencing().definitions
.
initial<ExampleClass>()
.analyzeTypeUsingReflection()
.generateJsonSchema()
.compileReferencingRoot()
.json
{
"$ref": "#/definitions/examples.ExampleClass
}
.compileReferencingRoot().definitions["examples.ExampleClass"]
):
{
"type": "object",
"required": [
"nested",
"number"
],
"properties": {
"nested": {
"$ref": "#/definitions/examples.NestedClass"
},
"number": {
"type": "integer",
"minimum": -2147483648,
"maximum": 2147483647
},
"text": {
"type": "string"
}
}
}
Configuration Options
Required Dependencies
implementation("io.github.smiley4:schema-kenerator-jsonschema:$version")
implementation 'io.github.smiley4:schema-kenerator-jsonschema:$version'
<dependency>
<groupId>io.github.smiley4</groupId>
<artifactId>schema-kenerator-jsonschema</artifactId>
<version>${version}</version>
</dependency>
Reference Path Type
Schemas can be referenced using different paths or "ids" when using compileReferencing
or compileReferencingRoot
RefType.FULL
: uses the full qualified version of the type names, e.g.some.examples.MyClass<kotlin.Int>
RefType.SIMPLE
: uses a shorter simple version of the type names, e.g.MyClass<Int>
Simple Path Type
initial<List<GenericClass<String>>>()
.analyzeTypeUsingReflection()
.generateJsonSchema()
.compileReferencing(pathType = RefType.SIMPLE)
{
"type": "array",
"items": {
"$ref": "#/definitions/GenericClass<String>"
}
}
Required Dependencies
implementation("io.github.smiley4:schema-kenerator-jsonschema:$version")
implementation 'io.github.smiley4:schema-kenerator-jsonschema:$version'
<dependency>
<groupId>io.github.smiley4</groupId>
<artifactId>schema-kenerator-jsonschema</artifactId>
<version>${version}</version>
</dependency>
Automatically Adding Titles
A "title" property can be automatically added to all types in the JSON Schema.
initial<List<GenericClass<String>>>()
.analyzeTypeUsingReflection()
.generateJsonSchema()
.withTitle(type = TitleType.SIMPLE)
.compileInlining()
{
"title": "List<GenericClass<String>>",
"type": "array",
"items": {
"title": "GenericClass<String>",
"type": "object",
"required": [ "value" ],
"properties": {
"value": {
"title": "String",
"type": "string"
}
}
}
}
The type of the title can be configured:
TitleType.FULL
: uses the full qualified version of the type names, e.g.some.examples.MyClass<kotlin.Int>
TitleType.SIMPLE
: uses a shorter simple version of the type names, e.g.MyClass<Int>
Required Dependencies
implementation("io.github.smiley4:schema-kenerator-jsonschema:$version")
implementation 'io.github.smiley4:schema-kenerator-jsonschema:$version'
<dependency>
<groupId>io.github.smiley4</groupId>
<artifactId>schema-kenerator-jsonschema</artifactId>
<version>${version}</version>
</dependency>
Schema-Kenerator Annotations
Additional properties of the JSON Schema can be filled with extra information from annotations, e.g. descriptions or default values. The handleCoreAnnotations()
-step adds information from annotations from schema-kenerator-core
to the schemas.
@Title("Annotated Class")
@Description("some description")
@Default("default value")
@Example("example 1")
@Deprecated
class CoreAnnotatedClass(
@Description("String field description")
@Default("A default String value")
@Example("An example of a String value")
val stringValue: String,
@Description("Int field description")
@Default("1111")
@Example("2222")
val intValue: Int,
)
initial<CoreAnnotatedClass>()
.analyzeTypeUsingReflection()
.generateJsonSchema()
.handleCoreAnnotations()
.compileInlining()
{
"title": "Annotated Class",
"description": "some description",
"type": "object",
"default": "default value",
"deprecated": true,
"examples": [ "example 1" ],
"required": [ "intValue", "stringValue" ],
"properties": {
"stringValue": {
"type": "string",
"description": "String field description",
"default": "A default String value",
"examples": [ "An example of a String value" ]
},
"intValue": {
"type": "integer",
"description": "Int field description",
"minimum": -2147483648,
"maximum": 2147483647,
"default": "1111",
"examples": [ "2222" ]
}
}
}
Required Dependencies
implementation("io.github.smiley4:schema-kenerator-core:$version")
implementation("io.github.smiley4:schema-kenerator-jsonschema:$version")
implementation 'io.github.smiley4:schema-kenerator-core:$version'
implementation 'io.github.smiley4:schema-kenerator-jsonschema:$version'
<dependency>
<groupId>io.github.smiley4</groupId>
<artifactId>schema-kenerator-core</artifactId>
<version>${version}</version>
</dependency>
<dependency>
<groupId>io.github.smiley4</groupId>
<artifactId>schema-kenerator-jsonschema</artifactId>
<version>${version}</version>
</dependency>
Jackson Annotations
Additional properties of the JSON Schema can be filled with extra information from annotations, e.g. descriptions or default values. The handleJacksonJsonSchemaAnnotations()
-step adds information from Jackson annotations to the schemas.
class JacksonAnnotatedClass(
@JsonPropertyDescription("Example description of the field")
val value: String
)
initial<JacksonAnnotatedClass>()
.analyzeTypeUsingReflection()
.generateJsonSchema()
.handleJacksonJsonSchemaAnnotations()
.compileInlining()
{
"type": "object",
"required": [ "value" ],
"properties": {
"value": {
"description": "Example description of the field"
"type": "string",
}
}
}
Required Dependencies
implementation("io.github.smiley4:schema-kenerator-jackson-jsonschema:$version")
implementation("io.github.smiley4:schema-kenerator-jsonschema:$version")
implementation 'io.github.smiley4:schema-kenerator-jackson-jsonschema:$version'
implementation 'io.github.smiley4:schema-kenerator-jsonschema:$version'
<dependency>
<groupId>io.github.smiley4</groupId>
<artifactId>schema-kenerator-jackson-jsonschema</artifactId>
<version>${version}</version>
</dependency>
<dependency>
<groupId>io.github.smiley4</groupId>
<artifactId>schema-kenerator-jsonschema</artifactId>
<version>${version}</version>
</dependency>