JSON Schema draft-07 validation over ocaml-json values
OCaml 91%
5%
Shell 2%
Dune 2%

README.md

nox-jsonschema #

JSON Schema draft-07 validation for nox-json values.

Every keyword of the draft-07 validation vocabulary is checked. $ref resolves within the schema and across the documents handed in by URI, never over the network. The draft-07 metaschema is bundled, and every schema is checked against it before use. Each error names the failing value and the failing keyword by JSON Pointer (RFC 6901).

Usage #

let schema =
  Json.Value.of_string_exn
    {|{"type": "object",
       "properties": {"tags": {"type": "array", "items": {"type": "string"}}}}|}

let t = Result.get_ok (Jsonschema.v schema)

let () =
  match Jsonschema.validate t (Json.Value.of_string_exn {|{"tags": ["a", 2]}|}) with
  | Error [ e ] ->
      assert (Jsonschema.instance e = "/tags/1");
      assert (
        Jsonschema.keyword e = "urn:nox-jsonschema:root#/properties/tags/items/type")
  | _ -> assert false

Jsonschema.pp_error prints that error as

/tags/1: integer is not of type string (urn:nox-jsonschema:root#/properties/tags/items/type)

A schema that references another document by URI receives it through ~resources, keyed by the absolute URI the reference resolves to:

let licence = Json.Value.of_string_exn {|{"enum": ["MIT", "ISC"]}|}

let t =
  Result.get_ok
    (Jsonschema.v
       ~resources:[ ("http://example.com/licence.json", licence) ]
       (Json.Value.of_string_exn
          {|{"$id": "http://example.com/root.json",
              "allOf": [{"$ref": "licence.json"}]}|}))

let () = assert (not (Jsonschema.is_valid t (Json.Value.string "GPL")))

Conformance #

The test suite replays the draft-7 files of the official JSON-Schema-Test-Suite verbatim, at a pinned commit (test/interop/json-schema-test-suite), with every required file and the optional format files for each format asserted. The optional files it does not run are named in test/interop/json-schema-test-suite/test.ml with the reason for each: ECMA-262 regular expressions beyond what Re.Perl compiles, idn-hostname and uri-template, which section 7.2 lets an implementation leave unchecked, and contentMediaType and contentEncoding, which section 8 makes annotations.

  • jsonschema (opam jsonschema, hence this package's nox- name) validates drafts 4 to 2020-12 over Yojson values. Nothing in this tree uses Yojson, and adopting it would put a second JSON representation, ppx_deriving_yojson and a conversion of every document into the closure of every consumer.
  • ocplib-json-typed and its successor json-data-encoding describe JSON Schema documents but do not validate instances against them.
  • Python's jsonschema is the reference the CycloneDX tooling uses; its check-jsonschema command is the oracle of ocaml-sbom's committed validation trace.