JSON Schema (draft 2020-12) parser and validator for Standard ML. Provides a
typed SML abstract syntax tree mirroring the JSON Schema keywords, a parser
that converts a Json.json value into a schema, and a validator that
checks an instance against a schema, producing a list of structural errors.
Part of the sjqtentacles monorepo of SML libraries. It depends on
sml-json (vendored, for the JSON
AST and parser) and sml-rederiv
(vendored, for the pattern keyword's regex matching).
- Schema ADT --
SObject,SArray,SString,SNumber,SBool,SNull,SRef,SAllOf,SAnyOf,SOneOf,SNot,SDefs,STrue,SConst,SEnummirroring the draft 2020-12 keywords. - Parser --
fromJson : Json.json -> schema resultwalks a JSON value and builds the typed schema tree. Unknown keywords are ignored (draft 2020-12 permits vendor extensions); malformed keyword values cause anErr. - Validator --
validate : schema -> Json.json -> error listchecks an instance against a schema. Each error carries a JSON-Pointer-stylepathand amessage. An empty list means the instance is valid. - Keyword coverage --
type,properties,required,additionalProperties,items,prefixItems,minItems,maxItems,minimum,maximum,exclusiveMinimum,exclusiveMaximum,minLength,maxLength,pattern(via sml-rederiv),enum,const,allOf,anyOf,oneOf,not,$ref,$defs.
Working and tested. The parser and validator cover all the keywords listed
above, exercised by a test suite spanning type validation, object/array
constraints, numeric bounds, pattern matching, enum/const, combinators, and
local $ref/$defs resolution.
Pure Standard ML using only the Basis library (plus the vendored sml-json
and sml-rederiv) -- no FFI, no threads. Verified on MLton and
Poly/ML, with identical, deterministic output across both.
make test # build + run the suite under MLton (default)
make test-poly # run the suite under Poly/ML
make all-tests # run under both
make clean(* Parse a schema from a JSON string. *)
val schemaJson = Json.parseJson
"{\"type\":\"object\",\"properties\":{\"name\":{\"type\":\"string\"}},\"required\":[\"name\"]}"
val schema = case schemaJson of
CharParsec.Ok v => (case Schema.fromJson v of
Schema.Ok s => s
| Schema.Err e => raise Fail e)
| CharParsec.Err e => raise Fail (CharParsec.errorToString e)
(* Validate an instance. *)
val instance = Json.parseJson "{\"name\":\"Alice\"}"
val errors = case instance of
CharParsec.Ok v => Schema.validate (schema, v)
| CharParsec.Err e => raise Fail (CharParsec.errorToString e)
(* errors = [] -- valid *)
val badInstance = Json.parseJson "{\"age\":30}"
val badErrors = case badInstance of
CharParsec.Ok v => Schema.validate (schema, v)
| CharParsec.Err e => raise Fail (CharParsec.errorToString e)
(* badErrors = [{path = "", message = "missing required property: name"}] *)| Function | Description |
|---|---|
Schema.schema |
The JSON Schema ADT (SObject, SArray, SString, ...). |
Schema.fromJson : Json.json -> schema result |
Parse a JSON value as a schema. |
Schema.validate : schema -> Json.json -> error list |
Validate an instance; empty list = valid. |
Schema.error |
{path: string, message: string}. |
sml-json(vendored, which vendorssml-parsec) -- JSON AST and parser.sml-rederiv(vendored) -- Brzozowski-derivative regex engine for thepatternkeyword.
smlpkg add github.com/sjqtentacles/sml-schema
smlpkg syncThen reference the library basis from your own .mlb:
lib/github.com/sjqtentacles/sml-schema/sml-schema.mlb
For Poly/ML, use the sources listed in sources.mlb in order (the vendored
sml-json and sml-rederiv first, then schema.sig and schema.sml).
MIT. See LICENSE.