{"id":23391421,"url":"https://github.com/ahrefs/ppx_deriving_jsonschema","last_synced_at":"2025-10-11T06:05:41.578Z","repository":{"id":265797179,"uuid":"852304458","full_name":"ahrefs/ppx_deriving_jsonschema","owner":"ahrefs","description":"Generate json schema from ocaml type","archived":false,"fork":false,"pushed_at":"2025-09-02T03:15:10.000Z","size":665,"stargazers_count":19,"open_issues_count":10,"forks_count":1,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-09-30T12:47:12.913Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"OCaml","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ahrefs.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGES.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2024-09-04T15:23:26.000Z","updated_at":"2025-06-28T04:17:29.000Z","dependencies_parsed_at":"2025-05-27T14:40:28.637Z","dependency_job_id":null,"html_url":"https://github.com/ahrefs/ppx_deriving_jsonschema","commit_stats":null,"previous_names":["ahrefs/ppx_deriving_jsonschema"],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/ahrefs/ppx_deriving_jsonschema","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ahrefs%2Fppx_deriving_jsonschema","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ahrefs%2Fppx_deriving_jsonschema/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ahrefs%2Fppx_deriving_jsonschema/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ahrefs%2Fppx_deriving_jsonschema/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ahrefs","download_url":"https://codeload.github.com/ahrefs/ppx_deriving_jsonschema/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ahrefs%2Fppx_deriving_jsonschema/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279006448,"owners_count":26084105,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-11T02:00:06.511Z","response_time":55,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-12-22T04:17:27.239Z","updated_at":"2025-10-11T06:05:41.573Z","avatar_url":"https://github.com/ahrefs.png","language":"OCaml","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ppx_deriving_jsonschema\n\n`ppx_deriving_jsonschema` is a PPX syntax extension that generates JSON schema from OCaml types.\n\nThe conversion aims to be compatible with the existing json derivers:\n- https://github.com/melange-community/melange-json\n- https://github.com/ocaml-ppx/ppx_deriving_yojson\n- https://github.com/janestreet/ppx_yojson_conv\n\n## Installation\n\n```sh\nopam install ppx_deriving_jsonschema\n```\n\n## `[@@deriving jsonschema]`\n\n```ocaml\ntype address = {\n  street: string;\n  city: string;\n  zip: string;\n} [@@deriving jsonschema]\n\ntype t = {\n  name: string;\n  age: int;\n  email: string option;\n  address: address;\n} [@@deriving jsonschema]\n\nlet schema = Ppx_deriving_jsonschema_runtime.json_schema t_jsonschema\n```\n\nSuch a type will be turned into a JSON schema like this:\n```json\n{\n  \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"address\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"zip\": { \"type\": \"string\" },\n        \"city\": { \"type\": \"string\" },\n        \"street\": { \"type\": \"string\" }\n      },\n      \"required\": [ \"zip\", \"city\", \"street\" ]\n    },\n    \"email\": { \"type\": \"string\" },\n    \"age\": { \"type\": \"integer\" },\n    \"name\": { \"type\": \"string\" }\n  },\n  \"required\": [ \"address\", \"age\", \"name\" ]\n}\n```\n\n### Conversion rules\n\n#### Basic types\n\nTypes `int`, `int32`, `int64`, `nativeint`, `string`, `bytes`, `float`, `bool` are converted to their JSON equivalents.\n\nType `char` is converted to `{ \"type\": \"string\",  \"minLength\": 1,  \"maxLength\": 1}`.\n\nType `'a ref` is treated as `'a`.\n\nType `unit` is converted to `{ \"type\": \"null\" }`.\n\n#### List and arrays\n\nOCaml lists and arrays are converted to `{ \"type\": \"array\", \"items\": { \"type\": \"...\" } }`.\n\n#### Tuples\n\nTuples are converted to `{ \"type\": \"array\", \"prefixItems\": [...] }`.\n\n```ocaml\ntype t = int * string [@@deriving jsonschema]\n```\n\n```json\n{\n  \"type\": \"array\",\n  \"prefixItems\": [ { \"type\": \"integer\" }, { \"type\": \"string\" } ],\n  \"unevaluatedItems\": false,\n  \"minItems\": 2,\n  \"maxItems\": 2\n}\n```\n\n#### Variants and polymorphic variants\n\nBy 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:\n\n```ocaml\ntype t =\n| Typ\n| Class of string\n[@@deriving jsonschema]\n```\n\n```json\n{\n  \"anyOf\": [\n    {\n      \"type\": \"array\",\n      \"prefixItems\": [ { \"const\": \"Typ\" } ],\n      \"unevaluatedItems\": false,\n      \"minItems\": 1,\n      \"maxItems\": 1\n    },\n    {\n      \"type\": \"array\",\n      \"prefixItems\": [ { \"const\": \"Class\" }, { \"type\": \"string\" } ],\n      \"unevaluatedItems\": false,\n      \"minItems\": 2,\n      \"maxItems\": 2\n    }\n  ]\n}\n```\n\nNote that the implicit tuple in a polymorphic variant is flattened. This can be disabled using the `~polymorphic_variant_tuple` flag.\n\n```ocaml\ntype a = [ `A of int * string * bool ] [@@deriving jsonschema]\n```\n\n```json\n{\n  \"anyOf\": [\n    {\n      \"type\": \"array\",\n      \"prefixItems\": [\n        { \"const\": \"A\" },\n        { \"type\": \"integer\" },\n        { \"type\": \"string\" },\n        { \"type\": \"boolean\" }\n      ],\n      \"unevaluatedItems\": false,\n      \"minItems\": 4,\n      \"maxItems\": 4\n    }\n  ]\n}\n```\n\n```ocaml\ntype b = [ `B of int * string * bool ] [@@deriving jsonschema ~polymorphic_variant_tuple]\n```\n\n```json\n{\n  \"anyOf\": [\n    {\n      \"type\": \"array\",\n      \"prefixItems\": [\n        { \"const\": \"B\" },\n        {\n          \"type\": \"array\",\n          \"prefixItems\": [\n            { \"type\": \"integer\" },\n            { \"type\": \"string\" },\n            { \"type\": \"boolean\" }\n          ],\n          \"unevaluatedItems\": false,\n          \"minItems\": 3,\n          \"maxItems\": 3\n        }\n      ],\n      \"unevaluatedItems\": false,\n      \"minItems\": 2,\n      \"maxItems\": 2\n    }\n  ]\n}\n```\n\nA `~variant_as_string` flag is exposed to obtain a more natural representation `\"anyOf\": [{ \"const\": \"...\" }, ...]`. This representation does _not_ support payloads. For example:\n\n```ocaml\ntype t =\n| Typ\n| Class of string\n[@@deriving jsonschema ~variant_as_string]\n```\n\n```json\n{ \"anyOf\": [ { \"const\": \"Typ\" }, { \"const\": \"Class\" } ] }\n```\n\nIf the JSON variant names differ from OCaml conventions, it is possible to specify the corresponding JSON string explicitly using `[@name \"constr\"]`, for example:\n\n```ocaml\ntype t =\n| Typ   [@name \"type\"]\n| Class of string [@name \"class\"]\n[@@deriving jsonschema ~variant_as_string]\n```\n\n```json\n{ \"anyOf\": [ { \"const\": \"type\" }, { \"const\": \"class\" } ] }\n```\n\n#### Records\n\nRecords are converted to `{ \"type\": \"object\", \"properties\": {...}, \"required\": [...], \"additionalProperties\": false }`.\n\nThe fields of type `option` are not included in the `required` list.\n\nBy default, additionalProperties are not allowed in objects. To allow additionalProperties, use the `allow_extra_fields` attribute:\n\n```ocaml\ntype company = {\n  name : string;\n  employees : int;\n}\n[@@deriving jsonschema]\n[@@jsonschema.allow_extra_fields]\n```\n\nThis annotation will generate a schema with `\"additionalProperties\": true`, allowing for additional fields not defined in the record:\n\n```json\n{\n  \"type\": \"object\",\n  \"properties\": {\n    \"name\": { \"type\": \"string\" },\n    \"age\": { \"type\": \"integer\" }\n  },\n  \"required\": [ \"name\", \"age\" ],\n  \"additionalProperties\": true\n}\n```\n\nWhen the JSON object keys differ from the ocaml field names, users can specify the corresponding JSON key implicitly using `[@key \"field\"]`, for example:\n\n```ocaml\ntype t = {\n  typ    : float [@key \"type\"];\n  class_ : float [@key \"CLASS\"];\n}\n[@@deriving jsonschema]\n```\n\n#### Inline Records in Variants\n\nYou can use the `[@jsonschema.allow_extra_fields]` attribute on a constructor with an inline record to allow additional fields in that record:\n\n```ocaml\ntype inline_record_with_extra_fields =\n  | User of { name : string; email : string } [@jsonschema.allow_extra_fields]\n  | Guest of { ip : string }\n[@@deriving jsonschema]\n```\n\nThis will generate a schema that allows additional fields for the `User` variant's record but not for the `Guest` variant:\n\n```json\n{\n  \"anyOf\": [\n    {\n      \"type\": \"array\",\n      \"prefixItems\": [\n        { \"const\": \"User\" },\n        {\n          \"type\": \"object\",\n          \"properties\": {\n            \"email\": { \"type\": \"string\" },\n            \"name\": { \"type\": \"string\" }\n          },\n          \"required\": [ \"email\", \"name\" ],\n          \"additionalProperties\": true\n        }\n      ],\n      \"unevaluatedItems\": false,\n      \"minItems\": 2,\n      \"maxItems\": 2\n    },\n    {\n      \"type\": \"array\",\n      \"prefixItems\": [\n        { \"const\": \"Guest\" },\n        {\n          \"type\": \"object\",\n          \"properties\": { \"ip\": { \"type\": \"string\" } },\n          \"required\": [ \"ip\" ],\n          \"additionalProperties\": false\n        }\n      ],\n      \"unevaluatedItems\": false,\n      \"minItems\": 2,\n      \"maxItems\": 2\n    }\n  ]\n}\n```\n\n#### References\n\nRather than inlining the definition of a type it is possible to use a [json schema `$ref`](https://json-schema.org/understanding-json-schema/structuring#dollarref) 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.\n\n```ocaml\ntype address = {\n  street : string;\n  city : string;\n  zip : string;\n}\n[@@deriving jsonschema]\n\ntype t = {\n  name : string;\n  age : int;\n  email : string option;\n  home_address : address; [@ref \"shared_address\"]\n  work_address : address; [@ref \"shared_address\"]\n  retreat_address : address; [@ref \"shared_address\"]\n}\n[@@deriving jsonschema]\n\nlet schema =\n  Ppx_deriving_jsonschema_runtime.json_schema\n    ~definitions:[(\"shared_address\", address_jsonschema)]\n    t_jsonschema\n```\n\nWould produce the following schema:\n```json\n{\n  \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\n  \"$defs\": {\n    \"shared_address\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"zip\": { \"type\": \"string\" },\n        \"city\": { \"type\": \"string\" },\n        \"street\": { \"type\": \"string\" }\n      },\n      \"required\": [ \"zip\", \"city\", \"street\" ]\n    }\n  },\n  \"type\": \"object\",\n  \"properties\": {\n    \"retreat_address\": { \"$ref\": \"#/$defs/shared_address\" },\n    \"work_address\": { \"$ref\": \"#/$defs/shared_address\" },\n    \"home_address\": { \"$ref\": \"#/$defs/shared_address\" },\n    \"email\": { \"type\": \"string\" },\n    \"age\": { \"type\": \"integer\" },\n    \"name\": { \"type\": \"string\" }\n  },\n  \"required\": [\n    \"retreat_address\", \"work_address\", \"home_address\", \"age\", \"name\"\n  ]\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fahrefs%2Fppx_deriving_jsonschema","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fahrefs%2Fppx_deriving_jsonschema","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fahrefs%2Fppx_deriving_jsonschema/lists"}