moonschema JSON Schema · MoonBit

Five versions, one walk

Every document,
thread by thread.

moonschema validates a JSON instance against a JSON Schema. You hand it a Json tree; it answers yes or no, and when the answer is no it says where — in the instance, and in the schema. Five versions of the specification, one validator, no sockets.

$ moon add moonbitstack/moonschema

In the browser, on the real engine

The loom

A schema on the left, an instance on the right. Edit either: the package compiled to JavaScript validates as you type, and prints every reason it had.

engine
warpschema.json
weftinstance.json
checking…

    5 versions draft-04 through 2020-12, one walk over the keywords.
    4 fields in a fault where in the instance, which keyword, what it said, where in the schema.
    0 sockets A reference reaches the documents you hand over, and nothing else.
    2 calls to a verdict Schema::new once, then valid or faults.

    What it holds to

    Six things it does properly

    The specification is large and most of it is small print. These are the parts that decide whether a validator is useful or merely present.

    $schema The document decides

    A document is read at the version it names, whatever the argument says. The argument is the fallback for a document that names nothing — never an override of one that does.

    Fault Two places, not one

    A fault names the place in the instance and the keyword that refused it, so a message can point at both. at is a JSON Pointer; so is schema_at.

    remotes No sockets

    A validator should not fetch. A $ref reaches a map of documents you fill in; what is not handed over does not resolve, and that is a fault, not a pass.

    unevaluated* Annotations carry

    What an applicator evaluated is passed to its neighbours, so unevaluatedProperties sees the branches of an allOf — which is the only way to close a composed object.

    pattern ECMA-262, not POSIX

    \d, \w, \s and \p{Letter} work as the specification writes them, read by moonregex's ECMA-262 dialect with the u flag; scripts and binary properties too, from Unicode 17.0.

    Refused Refuse, don't guess

    A $schema naming a version this does not know is refused when the schema is read — not read at a version of someone else's choosing.

    The five

    Versions, and why each is here

    Five released versions are still in use, so five are here. The value on the left is the enum; the URI is what a document writes in $schema.

    Draft$schemaWhat it is
    Draft4 json-schema.org/draft-04/schema# What OpenAPI 3.0 profiles, and what much Java and Python tooling still emits.
    Draft6 json-schema.org/draft-06/schema# $id replaces id; exclusiveMinimum becomes a number.
    Draft7 json-schema.org/draft-07/schema# The most widely deployed version.
    Draft2019 json-schema.org/draft/2019-09/schema Vocabularies, $defs, $recursiveRef, unevaluatedProperties.
    Draft2020 json-schema.org/draft/2020-12/schema $dynamicRef, prefixItems — and what OpenAPI 3.1 is.

    Both http and https spellings are accepted, with or without the trailing #.

    Where the cloth ends

    What it will not do

    Four decisions, each with a reason, so that nobody has to discover them by finding the hole.

    no Fetch a remote $ref

    A validator that opens sockets is a validator that blocks, retries and fails. The seam is remotes: a map from URI to document, filled by the caller.

    no Assert format

    The specification makes format an annotation unless a caller asks otherwise. This does not offer the otherwise yet, so nothing is asserted.

    no Hyper-schema

    Links and media types are a different specification. This one is validation.

    no Compile schemas to code

    Interpretation first. If it measures slow, that is the time to argue about it — not before there is a number.