package

github.com/spider-gazelle/json-schema

1.3.3 / published Jun 14, 2026 / repository

Describe crystal-lang JSON serializable types with JSON Schema

JSON Schema CI

A crystal lang tool for converting JSON serialisable class definitions into the JSON Schema representation.

Installation

dependencies:
  json-schema:
    github: spider-gazelle/json-schema

Usage

basic type support


require "json-schema"

String.json_schema #=> {type: "string"}

Int32.json_schema #=> {type: "integer", format: "Int32"}

Float32.json_schema #=> {type: "number", format: "Float32"}

Array(String | Int32).json_schema #=> { type: "array", items: { anyOf: [{type: "integer", format: "Int32"}, {type: "string"}] } }

# Works with enums
enum TestEnum
  Option1
  Option2
end

TestEnum.json_schema #=> {type: "string", enum: ["option1", "option2"]}

json serialisable support is included too, with deeply nested objects etc.


require "json-schema"

class MyType
  include JSON::Serializable

  getter string : String
  getter symbol : Symbol?
  getter time : Time
  getter integer : Int32
  getter union_type : String | Int64 | Bool
end

MyType.json_schema

# outputs

{
  type: "object",
  properties: {
    string:     {type: "string"},
    symbol:     {type: "string"},
    time:       {type: "string", format: "date-time"},
    integer:    {type: "integer", format: "Int32"},
    union_type: {anyOf: [{type: "boolean"}, {type: "integer", format: "Int64"}, {type: "string"}]}
  },
  required: ["string", "time", "integer", "union_type"]
}

You can also customize schema output using the @[JSON::Field] annotation


require "json-schema"

class MyType
  include JSON::Serializable

  # The `EpochConverter` here means the JSON value will actually be an integer
  # so to avoid the output being `type: "string", format: "date-time"` you can
  # supply a type override and custom format string.
  @[JSON::Field(converter: Time::EpochConverter, type: "integer", format: "Int64")]
  getter time : Time

  # or if you just want to provide a custom format
  @[JSON::Field(format: "email")]
  getter email : String
end

for anything too confusing it falls back to a generic { type: "object" } however this should only happen in some cases where you've inherited generic objects. e.g. class Me < Hash(String, Int32) (although this case is handled correctly)

API

  • Array(T)

    An Array is an ordered, integer-indexed collection of objects of type T.

  • Bool

    Bool has only two possible values: true and false.

  • Enum

    Enum is the base type of all enums.

  • Float

    Float is the base type of all floating point numbers.

  • Hash(K, V)

    A Hash represents a collection of key-value mappings, similar to a dictionary.

  • Int

    Int is the base type of all integer types.

  • JSON

    The JSON module allows parsing and generating JSON documents.

  • NamedTuple(**T)

    A named tuple is a fixed-size, immutable, stack-allocated mapping of a fixed set of keys to values.

  • Nil

    The Nil type has only one possible value: nil.

  • Set(T)

    Set implements a collection of values with no duplicates.

  • String

    A String represents an immutable sequence of UTF-8 characters.

  • Symbol

    A symbol is a constant that is identified by a name without you having to give it a numeric value.

  • Time

    Time represents a date-time instant in incremental time observed in a specific time zone.

  • Tuple(*T)

    A tuple is a fixed-size, immutable, stack-allocated sequence of values of possibly different types.

  • UUID

    Represents a UUID (Universally Unique IDentifier).