Skip to content

matthijscox/JSONSchemaGenerator.jl

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

37 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

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

Boolean Combination Keywords

JSONSchemaGenerator.jl provides a function combinationkeywords(::Type) which can be used to associate a struct with an array of special types AllOf{T,S}, AnyOf{T,S}, OneOf{T,S} and Not{T} that allow the corresponding JSON keyword to be generated in a schema (see Boolean JSON Schema combination). Note that more than two schemas can be combined by chaining: e.g. AllOf{A, AllOf{B, C}}.

In the following example we combine some schemas that check if fields are equal to specific values (using Val and Tuple types, noting that these do not serialize well and should only be used for validation purposes like this):

import JSONSchemaGenerator as JSG
using JSONSchema, JSON3

struct ConstantInt1Schema
    int::Val{1}
end

struct EnumInt2Or3Schema
    int::Tuple{2,3}
end

struct ConstantBoolTrueSchema
    bool::Val{true}
end

struct BooleanCombinationSchema
    int::Int
    bool::Bool
end
StructTypes.StructType(::Type{BooleanCombinationSchema}) = StructTypes.Struct()
JSG.combinationkeywords(::Type{BooleanCombinationSchema}) = [
    JSG.AllOf{
        JSG.AnyOf{ConstantInt1Schema, EnumInt2Or3Schema},
        JSG.Not{ConstantBoolTrueSchema}
    }
]

schema_dict = JSG.schema(BooleanCombinationSchema)

good_json = JSON3.write(BooleanCombinationSchema(2, false))
bad_json = JSON3.write(BooleanCombinationSchema(5, true))

JSONSchema.validate(JSONSchema.Schema(schema_dict), good_json) === nothing
JSONSchema.validate(JSONSchema.Schema(schema_dict), bad_json) !== nothing

The printed schema looks as follows:

julia> JSON.print(schema_dict, 2)
JSON.print(schema_dict, 2)
{
  "type": "object",
  "properties": {
    "int": {
      "type": "integer"
    },
    "bool": {
      "type": "boolean"
    }
  },
  "required": [
    "int",
    "bool"
  ],
  "allOf": [
    {
      "anyOf": [
        {
          "type": "object",
          "properties": {
            "int": {
              "const": 1
            }
          },
          "required": [
            "int"
          ]
        },
        {
          "type": "object",
          "properties": {
            "int": {
              "enum": [
                2,
                3
              ]
            }
          },
          "required": [
            "int"
          ]
        }
      ]
    },
    {
      "not": {
        "type": "object",
        "properties": {
          "bool": {
            "const": true
          }
        },
        "required": [
          "bool"
        ]
      }
    }
  ]
}

About

Create JSON Schemas directly from your Julia types

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors 3

  •  
  •  
  •  

Languages

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy