This is a minimal library to generate JSON schema from Scala ADTs. It is built on top of circe.
To reduce complexity and maintenance, this library does the absolute minimum: It will traverse your ADT and generate a schema with nested types and properties. No other fields are included.
Product types get the type object
and sum types are converted into anyOf
.
You can inject all other fields via annotation. As intended, there is zero validation--you can do whatever you want.
This library is built for Scala 3.
libraryDependencies += "com.melvinlow" %% "scala-json-schema" % "<VERSION>"
Encode a product:
import com.melvinlow.json.schema.*
import com.melvinlow.json.schema.generic.auto.given
case class Foo(x: Int, y: String)
JsonSchema[Foo].spaces2
// res0: String = """{
// "type" : "object",
// "properties" : {
// "x" : {
// "type" : "integer"
// },
// "y" : {
// "type" : "string"
// }
// }
// }"""
Encode a coproduct:
enum Bar:
case A, B
JsonSchema[Bar].spaces2
// res1: String = """{
// "anyOf" : [
// {
// "type" : "object",
// "properties" : {
//
// }
// },
// {
// "type" : "object",
// "properties" : {
//
// }
// }
// ]
// }"""
Add fields via annotation (it takes as input any String
and Json
key-value pair):
import com.melvinlow.json.schema.annotation.JsonSchemaField
import io.circe.Json
import io.circe.syntax.*
@JsonSchemaField("title", "dog".asJson)
@JsonSchemaField("required", Array("name").asJson)
case class Dog(
@JsonSchemaField("minLength", 1.asJson)
name: String
)
JsonSchema[Dog].spaces2
// res2: String = """{
// "required" : [
// "name"
// ],
// "title" : "dog",
// "type" : "object",
// "properties" : {
// "name" : {
// "minLength" : 1,
// "type" : "string"
// }
// }
// }"""
Create an encoder for a new type:
object H:
@JsonSchemaField("description", "my custom int".asJson)
opaque type MyInt = Int
given JsonSchemaEncoder[H.MyInt] with
def schema: Json =
val base = Json.obj("type" -> "integer".asJson)
val annotations = JsonSchemaField.onType[H.MyInt]
base.deepMerge(Json.obj(annotations*))
JsonSchema[H.MyInt].spaces2
// res3: String = """{
// "description" : "my custom int",
// "type" : "integer"
// }"""