package ppx_deriving_jsonschema
Install
Dune Dependency
Authors
Maintainers
Sources
sha256=d517fc97fabe39ef8eea49dcc91b9abec03e86fdcb53fcf37b046aa0954e4d1a
sha512=b9785ea7c0394946d4585839da4d137c2ea15642f2588df3c3506e514c08f4e8413a59dd075730d7e55e686dc165140c5cdce90e5eb349b439953dba9a142d76
README.md.html
ppx_deriving_jsonschema
ppx_deriving_jsonschema
is a PPX syntax extension that generates JSON schema from OCaml types.
The conversion aims to be compatible with the existing json derivers:
https://github.com/melange-community/melange-json
https://github.com/ocaml-ppx/ppx_deriving_yojson
https://github.com/janestreet/ppx_yojson_conv
Installation
opam install ppx_deriving_jsonschema
[@@deriving jsonschema]
type address = {
street: string;
city: string;
zip: string;
} [@@deriving jsonschema]
type t = {
name: string;
age: int;
email: string option;
address: address;
} [@@deriving jsonschema]
let schema = Ppx_deriving_jsonschema_runtime.json_schema t_jsonschema
Such a type will be turned into a JSON schema like this:
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"address": {
"type": "object",
"properties": {
"zip": { "type": "string" },
"city": { "type": "string" },
"street": { "type": "string" }
},
"required": [ "zip", "city", "street" ]
},
"email": { "type": "string" },
"age": { "type": "integer" },
"name": { "type": "string" }
},
"required": [ "address", "age", "name" ]
}
Conversion rules
Basic types
Types int
, int32
, int64
, nativeint
, string
, bytes
, float
, bool
are converted to their JSON equivalents.
Type char
is converted to { "type": "string", "minLength": 1, "maxLength": 1}
.
Type 'a ref
is treated as 'a
.
Type unit
is converted to { "type": "null" }
.
List and arrays
OCaml lists and arrays are converted to { "type": "array", "items": { "type": "..." } }
.
Tuples
Tuples are converted to { "type": "array", "prefixItems": [...] }
.
type t = int * string [@@deriving jsonschema]
{
"type": "array",
"prefixItems": [ { "type": "integer" }, { "type": "string" } ],
"unevaluatedItems": false,
"minItems": 2,
"maxItems": 2
}
Variants and polymorphic variants
By default, constructors in variants are represented as a list with one string, which is the name of the contructor. Constructors with arguments are represented as lists, the first element being the constructor name, the rest being its arguments. It reproduces the representation of ppx_deriving_yojson
and ppx_yojson_conv
. For example:
type t =
| Typ
| Class of string
[@@deriving jsonschema]
{
"anyOf": [
{
"type": "array",
"prefixItems": [ { "const": "Typ" } ],
"unevaluatedItems": false,
"minItems": 1,
"maxItems": 1
},
{
"type": "array",
"prefixItems": [ { "const": "Class" }, { "type": "string" } ],
"unevaluatedItems": false,
"minItems": 2,
"maxItems": 2
}
]
}
Note that the implicit tuple in a polymorphic variant is flattened. This can be disabled using the ~polymorphic_variant_tuple
flag.
type a = [ `A of int * string * bool ] [@@deriving jsonschema]
{
"anyOf": [
{
"type": "array",
"prefixItems": [
{ "const": "A" },
{ "type": "integer" },
{ "type": "string" },
{ "type": "boolean" }
],
"unevaluatedItems": false,
"minItems": 4,
"maxItems": 4
}
]
}
type b = [ `B of int * string * bool ] [@@deriving jsonschema ~polymorphic_variant_tuple]
{
"anyOf": [
{
"type": "array",
"prefixItems": [
{ "const": "B" },
{
"type": "array",
"prefixItems": [
{ "type": "integer" },
{ "type": "string" },
{ "type": "boolean" }
],
"unevaluatedItems": false,
"minItems": 3,
"maxItems": 3
}
],
"unevaluatedItems": false,
"minItems": 2,
"maxItems": 2
}
]
}
A ~variant_as_string
flag is exposed to obtain a more natural representation "anyOf": [{ "const": "..." }, ...]
. This representation does not support payloads. It reproduces the representation of melange-json
for enumeration like variants. For example:
type t =
| Typ
| Class of string
[@@deriving jsonschema ~variant_as_string]
{ "anyOf": [ { "const": "Typ" }, { "const": "Class" } ] }
If the JSON variant names differ from OCaml conventions, it is possible to specify the corresponding JSON string explicitly using [@name "constr"]
, for example:
type t =
| Typ [@name "type"]
| Class of string [@name "class"]
[@@deriving jsonschema ~variant_as_string]
{ "anyOf": [ { "const": "type" }, { "const": "class" } ] }
Records
Records are converted to { "type": "object", "properties": {...}, "required": [...] }
.
The fields of type option
are not included in the required
list.
When the JSON object keys differ from the ocaml field names, users can specify the corresponding JSON key implicitly using [@key "field"]
, for example:
type t = {
typ : float [@key "type"];
class_ : float [@key "CLASS"];
}
[@@deriving jsonschema]
References
Rather than inlining the definition of a type it is possible to use a json schema $ref
using the [@ref "name"]
attribute. In such a case, the type definition must be passed to Ppx_deriving_jsonschema_runtime.json_schema
as a parameter.
type address = {
street : string;
city : string;
zip : string;
}
[@@deriving jsonschema]
type t = {
name : string;
age : int;
email : string option;
home_address : address; [@ref "shared_address"]
work_address : address; [@ref "shared_address"]
retreat_address : address; [@ref "shared_address"]
}
[@@deriving jsonschema]
let schema =
Ppx_deriving_jsonschema_runtime.json_schema
~definitions:[("shared_address", address_jsonschema)]
t_jsonschema
Would produce the following schema:
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$defs": {
"shared_address": {
"type": "object",
"properties": {
"zip": { "type": "string" },
"city": { "type": "string" },
"street": { "type": "string" }
},
"required": [ "zip", "city", "street" ]
}
},
"type": "object",
"properties": {
"retreat_address": { "$ref": "#/$defs/shared_address" },
"work_address": { "$ref": "#/$defs/shared_address" },
"home_address": { "$ref": "#/$defs/shared_address" },
"email": { "type": "string" },
"age": { "type": "integer" },
"name": { "type": "string" }
},
"required": [
"retreat_address", "work_address", "home_address", "age", "name"
]
}