JSON Schema validation for MoonBit, draft-04 through v1: four output formats, nineteen format names, and vocabularies from outside.
moon add moonbitstack/moonschemafn main raise {
let schema = @moonschema.Schema::new(
@json.parse(
(
#|{ "type": "object",
#| "properties": { "port": { "type": "integer", "minimum": 1 } },
#| "required": ["port"] }
),
),
)
assert_eq(schema.valid(@json.parse("{\"port\": 8080}")), true)
let faults = schema.faults(@json.parse("{\"port\": 0}"))
assert_eq(faults.length(), 1)
assert_eq(
faults[0].to_string(),
"/port: below the minimum (minimum at /properties/port/minimum)",
)
}let schema = @moonschema.Schema::new(@json.parse("{ \"type\": \"integer\" }"))
let report = schema.report(@json.parse("\"seven\""), output=@moonschema.Output::Basic)
assert_eq(
report.to_json().stringify(indent=2),
#|{
#| "valid": false,
#| "errors": [
#| {
#| "valid": false,
#| "keywordLocation": "/type",
#| "instanceLocation": "",
#| "error": "not of type integer"
#| }
#| ]
#|},
)let schema = @moonschema.Schema::new(
@json.parse("{ \"format\": \"ipv4\" }"),
format=true,
)
assert_eq(schema.valid(@json.parse("\"not-an-ipv4\"")), false)
assert_eq(schema.valid(@json.parse("\"10.0.0.1\"")), true)let remote = @json.parse("{ \"$ref\": \"https://example.test/int\" }")
let schema = @moonschema.Schema::new(
remote,
remotes={
"https://example.test/int": @json.parse("{ \"type\": \"integer\" }"),
},
)
assert_eq(schema.valid(Json::number(1.0)), true)struct Even {}
pub impl @moonschema.Keyword for Even with fn name(_self) {
"x-even"
}
pub impl @moonschema.Keyword for Even with fn judge(_self, value, instance, site) {
ignore(site)
match (value, instance) {
(True, Number(number, ..)) =>
if number == (number / 2.0).floor() * 2.0 {
Accept
} else {
Refuse(why="odd")
}
_ => Accept
}
}
pub extend Even with @moonschema.Keyword::{name, judge}
struct Counting {}
pub impl @moonschema.Vocabulary for Counting with fn uri(_self) {
"https://example.test/vocab/even"
}
pub impl @moonschema.Vocabulary for Counting with fn keywords(_self) {
let even : &@moonschema.Keyword = Even::{ }
[even]
}
pub extend Counting with @moonschema.Vocabulary::{uri, keywords}let metaschema = @json.parse(
(
#|{ "$id": "https://example.test/meta",
#| "$schema": "https://json-schema.org/draft/2020-12/schema",
#| "$vocabulary": {
#| "https://json-schema.org/draft/2020-12/vocab/validation": true,
#| "https://example.test/vocab/even": true } }
),
)
let remotes = { "https://example.test/meta": metaschema }
let registry = @moonschema.Registry::new()
registry.add(Counting::{ })
let schema = @moonschema.Schema::new(
@json.parse(
(
#|{ "$schema": "https://example.test/meta",
#| "type": "integer", "x-even": true }
),
),
remotes~,
registry~,
)
assert_eq(schema.valid(@json.parse("4")), true)
assert_eq(
schema.faults(@json.parse("5"))[0].to_string(),
"the instance: odd (x-even at /x-even)",
)struct Parity {}
pub impl @moonschema.Keyword for Parity with fn name(_self) {
"x-parity"
}
pub impl @moonschema.Keyword for Parity with fn judge(
_self,
value,
instance,
site,
) {
ignore(site)
match (value, instance) {
(True, Number(number, ..)) =>
if number == (number / 2.0).floor() * 2.0 {
Accept
} else {
Refuse(why="odd")
}
_ => Accept
}
}
pub extend Parity with @moonschema.Keyword::{name, judge}
struct ParityVocabulary {}
pub impl @moonschema.Vocabulary for ParityVocabulary with fn uri(_self) {
"https://example.test/vocab/parity"
}
pub impl @moonschema.Vocabulary for ParityVocabulary with fn keywords(_self) {
let parity : &@moonschema.Keyword = Parity::{ }
[parity]
}
pub extend ParityVocabulary with @moonschema.Vocabulary::{uri, keywords}
test {
let metaschema = @json.parse(
(
#|{ "$id": "https://example.test/meta",
#| "$schema": "https://json-schema.org/draft/2020-12/schema",
#| "$vocabulary": {
#| "https://json-schema.org/draft/2020-12/vocab/validation": true,
#| "https://example.test/vocab/parity": true } }
),
)
let registry = @moonschema.Registry::new()
registry.add(ParityVocabulary::{ })
let schema = @moonschema.Schema::new(
@json.parse(
(
#|{ "$schema": "https://example.test/meta",
#| "type": "integer", "x-parity": true }
),
),
remotes={ "https://example.test/meta": metaschema },
registry~,
)
assert_eq(schema.valid(@json.parse("4")), true)
assert_eq(schema.faults(@json.parse("5"))[0].message, "odd")
}pub(all) struct OutputUnit {
valid : Bool
keyword_at : String
absolute_at : String?
at : String
error : String?
annotation : Json?
children : Array[OutputUnit]
} derive(Eq, Debug)impl ToJson for OutputUnitpub struct Registry {
// private fields
}pub struct Schema {
// private fields
}test {
let schema = @moonschema.Schema::new(
@json.parse("{ \"type\": \"integer\", \"minimum\": 1 }"),
)
let faults = schema.faults(@json.parse("0"))
assert_eq(faults.length(), 1)
assert_eq(faults[0].at, "")
assert_eq(faults[0].keyword, "minimum")
assert_eq(faults[0].schema_at, "/minimum")
assert_eq(faults[0].message, "below the minimum")
}test {
let schema = @moonschema.Schema::new(
@json.parse("{ \"type\": \"object\", \"required\": [\"id\"] }"),
)
assert_eq(schema.valid(@json.parse("{\"id\": 1}")), true)
assert_eq(schema.valid(@json.parse("{}")), false)
}test {
let schema = @moonschema.Schema::new(
@json.parse("{ \"type\": \"integer\", \"minimum\": 1 }"),
)
let report = schema.report(@json.parse("0"), output=@moonschema.Output::Basic)
assert_eq(report.valid, false)
assert_eq(report.units[0].keyword_at, "/minimum")
assert_eq(report.units[0].error, Some("below the minimum"))
}test {
let schema = @moonschema.Schema::new(@json.parse("{ \"type\": \"integer\" }"))
assert_eq(schema.valid(@json.parse("7")), true)
assert_eq(schema.valid(@json.parse("\"seven\"")), false)
}Install
Download zipJSON Schema validation for MoonBit, draft-04 through v1: four output formats, nineteen format names, and vocabularies from outside.