JSONSchemaGenerator.jl

Create JSON Schemas directly from your Julia types
Author matthijscox
Popularity
3 Stars
Updated Last
4 Months Ago
Started In
March 2022

JSONSchemaGenerator

Create minimal JSON schemas from custom Julia types.

Current restrictions:

  • no parametric types
  • no Union types, except Union{Nothing, T} for optional fields
  • no abstract types in fields, only concrete types
  • must define StructTypes.StructType for your custom types if you want to use_references=true
  • must define StructTypes.omitempties for optional fields

Example usage

using JSONSchemaGenerator, StructTypes

struct OptionalFieldSchema
    int::Int
    optional::Union{Nothing, String}
end
StructTypes.StructType(::Type{OptionalFieldSchema}) = StructTypes.Struct()
StructTypes.omitempties(::Type{OptionalFieldSchema}) = (:optional,)

struct NestedFieldSchema
    int::Int
    field::OptionalFieldSchema
    vector::Vector{OptionalFieldSchema}
end
StructTypes.StructType(::Type{NestedFieldSchema}) = StructTypes.Struct()

schema_dict = JSONSchemaGenerator.schema(NestedFieldSchema)

You can easily print the schema with JSON or JSON3

julia> using JSON

julia> JSON.print(schema_dict, 2)
{
  "type": "object",
  "properties": {
    "int": {
      "type": "integer"
    },
    "field": {
      "type": "object",
      "properties": {
        "int": {
          "type": "integer"
        },
        "optional": {
          "type": "string"
        }
      },
      "required": [
        "int"
      ]
    },
    "vector": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "int": {
            "type": "integer"
          },
          "optional": {
            "type": "string"
          }
        },
        "required": [
          "int"
        ]
      }
    }
  },
  "required": [
    "int",
    "field",
    "vector"
  ]
}

Referenced JSON schema types.

By default the generated schema is recursively nested, meaning that any repeating type will be generated multiple times. The use_references=true keyword argument can generate the JSON references for you. You can see that the OptionalFieldSchema is now referenced with $ref towards the $defs section of the schema instead of being copied.

julia> schema_dict = JSONSchemaGenerator.schema(NestedFieldSchema, use_references=true);

julia> JSON.print(schema_dict, 2)
{
  "type": "object",
  "properties": {
    "int": {
      "type": "integer"
    },
    "field": {
      "$ref": "#/$defs/OptionalFieldSchema"
    },
    "vector": {
      "type": "array",
      "items": {
        "$ref": "#/$defs/OptionalFieldSchema"
      }
    }
  },
  "required": [
    "int",
    "field",
    "vector"
  ],
  "$defs": {
    "OptionalFieldSchema": {
      "type": "object",
      "properties": {
        "int": {
          "type": "integer"
        },
        "optional": {
          "type": "string"
        }
      },
      "required": [
        "int"
      ]
    }
  }
}

Schema Validation

JSONSchema.jl provides validation for JSON schemas. The schema dictionary generated by JSONSchemaGenerator.jl works together with JSONSchema.jl.

JSONSchema.jl works with JSON.jl parsing, but JSON3.jl is better for direct JSON (de)serialization with StructTypes.jl definitions, so unfortunately you may need to use both JSON.jl and JSON3.jl, especially if you have optional fields defined with Union{Nothing, T}.

Let's use the example above to generate a JSON string and validate it:

using JSONSchema, JSON3, JSON
schema_dict = JSONSchemaGenerator.schema(NestedFieldSchema, use_references=true)

obj = NestedFieldSchema(
    1,
    OptionalFieldSchema(2, nothing),
    [OptionalFieldSchema(2, "string"), OptionalFieldSchema(2, nothing)]
)

# parsing back into a Dict, because that is what JSONSchema.validate wants
json_dict = JSON3.write(obj) |> JSON.parse

JSONSchema.validate(JSONSchema.Schema(schema_dict), json_dict) === nothing

Used By Packages

No packages found.