moonschema

    JSON Schema validation for MoonBit, draft-04 to 2020-12.

    json-schema
    schema
    validation
    openapi
    json
    moonbit
    Download zip
    Version
    0.2.1
    License
    Apache-2.0
    Last updated
    2 hours ago
    Downloads
    710

    moonschema

    moon add moonbitstack/moonschema

    fn main raise {
    let schema = @moonschema.Schema::new(
    @json.parse(
    (
    #|{ "type": "object",
    #| "properties": { "port": { "type": "integer", "minimum": 1 } },
    #| "required": ["port"] }
    ),
    ),
    )
    println(schema.valid(@json.parse("{\"port\": 8080}")))
    for fault in schema.faults(@json.parse("{\"port\": 0}")) {
    println(fault.to_string())
    }
    }

    true /port: below the minimum (minimum at /properties/port/minimum)

    #What it answers

    Schema::new reads the document once. After that, valid is the flag output and faults is the basic output — every reason, not just the first.

    #A fault

    Four fields, so a message can point at both ends of a refusal.

    #Versions

    A document is read at the version it names in $schema. The draft argument is the fallback for a document that names nothing; it never overrides one that does. A $schema this does not know is refused, not guessed at.

    Both http and https spellings are accepted, with or without the trailing #. Draft::of(uri) reads one back; Draft::uri() prints the one a version writes.

    #References

    A $ref is resolved with the RFC 3986 rules a browser uses for a relative link, against the base URI in effect — the document's $id, or the one a surrounding subschema gave it. $anchor names a place inside a document by a plain word.

    A reference to another document resolves only when that document is handed over. Nothing is fetched: a validator that opens sockets is a validator that blocks, retries and fails. What is not handed over does not resolve, and a reference that lands nowhere is a fault rather than a silent pass.

    let remote = @json.parse("{ \"$ref\": \"https://example.test/int\" }")
    let schema = @moonschema.Schema::new(
    remote,
    remotes={
    "https://example.test/int": @json.parse("{ \"type\": \"integer\" }"),
    },
    )
    println(schema.valid(Json::number(1.0))) // true

    #What it will not do

    • Fetch a remote $ref. The seam is remotes; the caller fills it from wherever they keep them.
    • Assert format. The specification makes it an annotation unless a caller asks otherwise, and this does not offer the otherwise yet.
    • Hyper-schema. Links and media types are a different specification.
    • Compile schemas to code. Interpretation first; a measurement is the time to argue about it.

    #Examples

    The first four are programs with their output, run with moon run src/examples/<name>. The last is the library the documentation site compiles to JavaScript, built with moon build --target js --release src/examples/web.

    #Licence

    Apache-2.0.

    Refused

    pub(all) suberror Refused {
    Unknown(String)
    Broken(at~ : String, why~ : String)
    } derive(Eq,
    Debug
    )

    Why a schema could not be read.

    These are faults in the schema, not in the instance an instance that fails validation produces [Fault]s instead.
    impl Show for Refused

    Refused::equal

    fn Refused::equal(Refused, Refused) -> Bool

    Refused::not_equal

    fn Refused::not_equal(x : Refused, y : Refused) -> Bool

    Refused::output

    fn Refused::output(self : Refused, logger : &Logger) -> Unit

    Refused::to_repr

    Refused::to_string

    fn Refused::to_string(self : Refused) -> String

    Draft

    pub(all) enum Draft {
    Draft4
    Draft6
    Draft7
    Draft2019
    Draft2020
    } derive(Eq,
    Debug
    )

    Which version of JSON Schema a document is written against.

    The five released versions that are still in use. draft-03 is not here: it predates the $ keywords and nothing has been written against it since 2010. The next release is not here yet either; it arrives as another value of this enum and a keyword table, not as a second validator.
    impl Show for Draft

    Draft::equal

    fn Draft::equal(Draft, Draft) -> Bool

    Draft::not_equal

    fn Draft::not_equal(x : Draft, y : Draft) -> Bool

    Draft::of

    fn Draft::of(uri : StringView) -> Draft?

    The version a $schema URI names.

    It answers None for a URI it does not know rather than falling back to a version of its own choosing: a document written against something else is not a document to guess at. Both the http and https spellings are accepted, and a trailing # is optional, because both are found in the wild.

    Draft::output

    fn Draft::output(self : Draft, logger : &Logger) -> Unit

    Draft::to_repr

    Draft::to_string

    fn Draft::to_string(self : Draft) -> String

    Draft::uri

    fn Draft::uri(self : Draft) -> String

    The $schema URI that names this version.

    Fault

    pub(all) struct Fault {
    at : String
    schema_at : String
    keyword : String
    message : String
    } derive(Eq,
    Debug
    )

    One reason an instance failed, in the shape the basic output gives it (2020-12 §12.4.2).
    impl Show for Fault

    Fault::equal

    fn Fault::equal(Fault, Fault) -> Bool

    Fault::not_equal

    fn Fault::not_equal(x : Fault, y : Fault) -> Bool

    Fault::output

    fn Fault::output(self : Fault, logger : &Logger) -> Unit

    Fault::to_repr

    Fault::to_string

    fn Fault::to_string(self : Fault) -> String

    Output

    pub(all) enum Output {
    Flag
    Basic
    Detailed
    Verbose
    } derive(Eq,
    Debug
    )

    How much a validation says when it fails.

    The four formats the specification itself defines (2020-12 §12.4), named as it names them.
    impl Show for Output

    Output::equal

    fn Output::equal(Output, Output) -> Bool

    Output::not_equal

    fn Output::not_equal(x : Output, y : Output) -> Bool

    Output::output

    fn Output::output(self : Output, logger : &Logger) -> Unit

    Output::to_repr

    Output::to_string

    fn Output::to_string(self : Output) -> String

    Schema

    pub struct Schema {
    // private fields
    }

    A schema document, read and ready to validate instances against.

    The version is the one the document names in $schema, whatever draft said; a document is more specific than a setting. remotes are the documents a $ref may reach outside this one, by the URI each is registered under: this library opens no sockets, so what is not fed to it does not exist.

    Schema::faults

    fn Schema::faults(self : Schema, instance : Json) -> Array[Fault]

    Every reason instance failed, or an empty array when it validated.

    This is the basic output (§12.4.2), flattened: each fault says where in the instance it was, which keyword refused it, and where that keyword was.

    let schema = @moonschema.Schema::new(
    @json.parse("{ \"type\": \"integer\", \"minimum\": 1 }"),
    )
    for fault in schema.faults(@json.parse("0")) {
    println(fault.to_string())
    }
    // the instance: below the minimum (minimum at /minimum)

    Schema::new

    fn Schema::new(document : Json, draft? : Draft, remotes? : Map[String, Json]) -> Schema raise Refused

    Read a schema document.

    draft is the version to read it at when it does not name one; a $schema it does name wins. remotes are the documents a $ref may reach, each under the URI it is referred to by.

    let schema = @moonschema.Schema::new(
    @json.parse("{ \"type\": \"object\", \"required\": [\"id\"] }"),
    )
    schema.valid(@json.parse("{\"id\": 1}")) // true

    Schema::valid

    fn Schema::valid(self : Schema, instance : Json) -> Bool

    Whether instance validates.

    This is the flag output (§12.4.1): the cheapest answer, and the one a guard on a request wants. [Schema::faults] says why not.

    let schema = @moonschema.Schema::new(@json.parse("{ \"type\": \"integer\" }"))
    schema.valid(@json.parse("7")) // true
    schema.valid(@json.parse("\"seven\"")) // false

    draft

    let draft : Draft

    The version assumed when a document does not name one.

    2020-12: the current release, and the one OpenAPI 3.1 is defined against. A document that says $schema is read at the version it says, whatever this is set to — the document is more specific than the setting.

    output

    let output : Output

    The output format a validation produces unless asked for another.

    Flag, because the common case is a guard that wants a yes or a no and collecting errors nobody reads is work nobody asked for. A caller that wants to tell someone what went wrong asks for Basic.

    Powered by MoonBit

    Site sourceReport issueModulesSkillsBuild queueStatistics

    © 2026 mooncakes.io