Introducing Kotlinx-schema: Generate JSON Schema from Kotlin Types and Functions
Table of Contents
Introducing Kotlinx-schema #
Kotlinx-schema is an experimental JetBrains library that automates JSON Schema generation from Kotlin and Java code. It eliminates schema drift by deriving schemas directly from your source, ensuring that API documentation and implementation stay synchronized as the codebase evolves.
Unlike traditional reflection-based tools, Kotlinx-schema provides both runtime reflection and compile-time generation via KSP. This KSP integration is essential for Kotlin Multiplatform (KMP) targets where runtime reflection is limited or unavailable, allowing you to ship schemas as part of the build process.
Core capabilities include:
- Polymorphic Class Schemas: Support for nested and sealed classes.
- Function Signatures: Automatic schema extraction for method parameters, optimized for LLM tool calling.
- Metadata Extraction: Seamless integration with KDoc (via KSP) and annotations to populate field descriptions.
- Nullability Control: Configurable mapping of nullable types to optional fields or JSON Schema union types.
- JSON Schema DSL: A programmatic model for building or manipulating schemas manually.
To integrate Kotlinx-schema, configure the KSP plugin and annotate your types to start generating synchronized schemas during your build.
Configuration #
To enable KSP-based schema generation in a Kotlin Multiplatform project, add the following to your build.gradle.kts:
plugins {
kotlin("multiplatform")
kotlin("plugin.serialization")
id("com.google.devtools.ksp") version "2.3.5"
}
// Check the latest version on Maven Central
val kotlinxSchemaVersion = "0.1.0"
dependencies {
add("kspCommonMainMetadata", "org.jetbrains.kotlinx:kotlinx-schema-ksp:$kotlinxSchemaVersion")
}
kotlin {
jvm()
js {
nodejs()
}
sourceSets {
commonMain {
// Register source dir for generated sources
kotlin.srcDir("build/generated/ksp/metadata/commonMain/kotlin")
dependencies {
implementation(libs.kotlinx.serialization.json)
// Required for annotating classes to process
implementation("org.jetbrains.kotlinx:kotlinx-schema-annotations:$kotlinxSchemaVersion")
}
}
}
}
ksp {
// Generate JsonSchema extension property along with string representation of the schema
arg("kotlinx.schema.withSchemaObject", "true")
// Process classes from this package
arg("kotlinx.schema.rootPackage", "com.example.shapes")
}
For more details, see the quickstart guide and the KSP setup guide.
Generating Class Schemas #
The following example demonstrates JSON Schema generation for a sealed class hierarchy:
package com.example.shapes
import kotlinx.schema.Description
import kotlinx.schema.Schema
/**
* A geometric shape. This sealed class demonstrates polymorphic schema generation.
*/
@Schema
sealed class Shape {
@Description("A name for this shape")
abstract val name: String
/**
* A circle defined by its radius.
*/
@Schema
data class Circle(
override val name: String,
@Description("Radius in units (must be positive)")
val radius: Double,
) : Shape()
/**
* A rectangle with width and height.
*/
@Schema
data class Rectangle(
override val name: String,
@Description("Width in units (must be positive)")
val width: Double,
@Description("Height in units (must be positive)")
val height: Double,
) : Shape()
}
After building the project, the KSP processor generates extension properties you can use to obtain the schema as a string or as a JsonObject.
import com.example.shapes.Shape
import com.example.shapes.jsonSchemaString
import kotlinx.serialization.json.Json
fun main() {
println(Shape::class.jsonSchemaString)
}
The generated schema is:
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "com.example.shapes.Shape",
"description": "A geometric shape. This sealed class demonstrates polymorphic schema generation.",
"type": "object",
"additionalProperties": false,
"oneOf": [
{
"$ref": "#/$defs/com.example.shapes.Shape.Circle"
},
{
"$ref": "#/$defs/com.example.shapes.Shape.Rectangle"
}
],
"$defs": {
"com.example.shapes.Shape.Circle": {
"type": "object",
"description": "A circle defined by its radius.",
"properties": {
"name": {
"type": "string"
},
"radius": {
"type": "number",
"description": "Radius in units (must be positive)"
}
},
"required": [
"name",
"radius"
],
"additionalProperties": false
},
"com.example.shapes.Shape.Rectangle": {
"type": "object",
"description": "A rectangle with width and height.",
"properties": {
"name": {
"type": "string"
},
"width": {
"type": "number",
"description": "Width in units (must be positive)"
},
"height": {
"type": "number",
"description": "Height in units (must be positive)"
}
},
"required": [
"name",
"width",
"height"
],
"additionalProperties": false
}
}
}
The generated schema also incorporates information from KDoc!
KSP generates extensions for all classes annotated with @Schema.
You can also use the jsonSchema extension property:
val jsonSchema: JsonObject = Shape::class.jsonSchema
Generating Function Schemas #
The library also supports schema generation for function signatures:
/**
* Greets the user with a personalized message.
*
* @param name the name of the person to greet
* @return a greeting message addressed to the specified name
*/
@Schema
internal fun sayHello(
@Description("Name to greet") name: String?,
): String = "Hello, ${name ?: "friend"}!"
This is particularly useful when you need a JSON Schema representation of callable tools (for example, in LLM integrations).
import com.example.shapes.sayHelloJsonSchema
import com.example.shapes.sayHelloJsonSchemaString
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject
private val json = Json { prettyPrint = true }
fun main() {
val functionCallSchemaString: String = sayHelloJsonSchemaString()
val functionCallSchema: JsonObject = sayHelloJsonSchema()
println("$functionCallSchemaString\n")
println("${functionCallSchema::class}: ${json.encodeToString(functionCallSchema)}")
}
The resulting JSON representation includes both the raw string and the JsonObject model:
{"type":"function","name":"sayHello","description":"Greets the user with a personalized message.","strict":true,"parameters":{"type":"object","properties":{"name":{"type":["string","null"],"description":"Name to greet"}},"required":["name"],"additionalProperties":false}}
class kotlinx.serialization.json.JsonObject: {
"type": "function",
"name": "sayHello",
"description": "Greets the user with a personalized message.",
"strict": true,
"parameters": {
"type": "object",
"properties": {
"name": {
"type": [
"string",
"null"
],
"description": "Name to greet"
}
},
"required": [
"name"
],
"additionalProperties": false
}
}
Conclusion #
Kotlinx-schema provides a type-safe, compile-time approach to JSON Schema generation, ensuring that API definitions and implementation stay in sync. Its modular architecture allows plugging in additional metadata sources and targeting new schema or serialization formats, making it a robust choice for modern Kotlin development—especially when targeting Kotlin Multiplatform or integrating with LLMs.
As an experimental project, community feedback is essential for shaping its future. ✨
Resources #
- GitHub: Kotlin/kotlinx-schema
- Maven Central: Artifacts
- API Reference: Documentation
- Samples: Project Examples