Last active
August 2, 2022 08:04
-
-
Save counter2015/e2a6ac2f645504e7e092548b9575664a to your computer and use it in GitHub Desktop.
A json-schema draft
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import cats.syntax.functor._ | |
import io.circe.generic.extras.semiauto.{deriveConfiguredDecoder, deriveConfiguredEncoder} | |
import io.circe.generic.extras.{Configuration, ConfiguredJsonCodec} | |
import io.circe.syntax._ | |
import io.circe.{Decoder, Encoder, Json} | |
// https://json-schema.org/draft/2020-12/json-schema-validation.html#rfc.section.6 | |
sealed trait JsonSchemaValue | |
object JsonSchemaValue { | |
implicit val config: Configuration = Configuration.default.withStrictDecoding | |
implicit val encoder: Encoder[JsonSchemaValue] = Encoder.instance { | |
case v: Reference => v.asJson | |
case v: JsonSchema => v.asJson | |
} | |
implicit val decoder: Decoder[JsonSchemaValue] = List[Decoder[JsonSchemaValue]]( | |
Decoder[Reference].widen, | |
Decoder[JsonSchema].widen | |
).reduceLeft(_ or _) | |
@ConfiguredJsonCodec | |
case class Reference($ref: String) extends JsonSchemaValue | |
/** `asJson` will return a Json object with the following properties, remove any null values on object | |
*/ | |
case class JsonSchema( | |
$id: Option[String] = None, | |
$schema: Option[String] = None, // Some("https://json-schema.org/draft/2020-12/schema") | |
title: Option[String] = None, | |
description: Option[String] = None, | |
`type`: Option[JType] = None, | |
properties: Option[Map[String, JsonSchemaValue]] = None, | |
allOf: Option[List[JsonSchemaValue]] = None, | |
oneOf: Option[List[JsonSchemaValue]] = None, | |
anyOf: Option[List[JsonSchemaValue]] = None, | |
items: Option[JsonSchemaValue] = None, | |
`enum`: Option[List[Json]] = None, | |
examples: Option[List[String]] = None, | |
format: Option[String] = None, | |
required: Option[List[String]] = None, | |
minimum: Option[Int] = None, | |
exclusiveMinimum: Option[Int] = None, | |
maximum: Option[Int] = None, | |
exclusiveMaximum: Option[Int] = None, | |
minLength: Option[Int] = None, | |
maxLength: Option[Int] = None, | |
pattern: Option[String] = None, | |
const: Option[Json] = None, | |
default: Option[Json] = None, | |
multipleOf: Option[Int] = None // the value must be positive | |
) extends JsonSchemaValue | |
object JsonSchema { | |
implicit val encoder: Encoder[JsonSchema] = deriveConfiguredEncoder[JsonSchema].mapJson(_.dropNullValues) | |
implicit val decoder: Decoder[JsonSchema] = deriveConfiguredDecoder[JsonSchema] | |
} | |
def textOut(schema: JsonSchemaValue): String = schema.asJson.spaces4 | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import io.circe.{Decoder, Encoder} | |
import io.circe.syntax._ | |
sealed trait JType | |
/* | |
etype = "number" | "object" | "integer" | "string" | "null" | "array" | |
jtype = etype | array[etype] | |
see: https://json-schema.org/draft/2020-12/json-schema-validation.html#rfc.section.6.1.1 | |
*/ | |
object JType { | |
case class StringType(value: SchemaTypeEnum) extends JType | |
case class StringListType(value: List[SchemaTypeEnum]) extends JType | |
implicit val encoder: Encoder[JType] = Encoder.instance { | |
case v: StringType => v.value.asJson | |
case v: StringListType => v.value.asJson | |
} | |
implicit val decoder: Decoder[JType] = List[Decoder[JType]]( | |
SchemaTypeEnum.decoder.map(StringType), | |
Decoder.decodeList[SchemaTypeEnum].map(StringListType) | |
).reduceLeft(_ or _) | |
val default: StringType = StringType(SchemaTypeEnum.`object`) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import io.circe._ | |
sealed trait SchemaTypeEnum | |
object SchemaTypeEnum { | |
case object `null` extends SchemaTypeEnum | |
case object `boolean` extends SchemaTypeEnum | |
case object `object` extends SchemaTypeEnum | |
case object `array` extends SchemaTypeEnum | |
sealed trait `number` extends SchemaTypeEnum | |
case object `number` extends `number` | |
case object `string` extends SchemaTypeEnum | |
case object `integer` extends `number` | |
implicit val encoder: Encoder[SchemaTypeEnum] = Encoder.instance { | |
case `null` => Json.fromString("null") | |
case `boolean` => Json.fromString("boolean") | |
case `object` => Json.fromString("object") | |
case `array` => Json.fromString("array") | |
case `number` => Json.fromString("number") | |
case `string` => Json.fromString("string") | |
case `integer` => Json.fromString("integer") | |
} | |
implicit val decoder: Decoder[SchemaTypeEnum] = (c: HCursor) => { | |
c.as[String] match { | |
case Right("null") => Right(`null`) | |
case Right("boolean") => Right(`boolean`) | |
case Right("object") => Right(`object`) | |
case Right("array") => Right(`array`) | |
case Right("number") => Right(`number`) | |
case Right("string") => Right(`string`) | |
case Right("integer") => Right(`integer`) | |
case Right(value) => Left(DecodingFailure(s"\"$value\" is not a valid type of json schema", c.history)) | |
case Left(value) => Left(DecodingFailure(s"\"$value\" is not a valid type of json schema", c.history)) | |
} | |
} | |
def fromString(str: String): SchemaTypeEnum = str match { | |
case "null" => `null` | |
case "boolean" => `boolean` | |
case "object" => `object` | |
case "array" => `array` | |
case "number" => `number` | |
case "string" => `string` | |
case "integer" => `integer` | |
case _ => `object` | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment