A real-life example
Named records, a required array, an optional field, and recursion-free reuse — built once, validated across formats.
An order schema, combining several of the ideas from earlier steps into one working example:
ORDER = '''
record Address { "street": string, "city": string }
record LineItem { "sku": string, "qty": integer, "price": number }
record Order {
"id": string,
"status": string,
"total": number,
"address": Address,
"items" [1,]: LineItem, # at least one line item
"coupon" [0,1]: string, # optional
}
root Order
'''
s = parse_schema(ORDER)The records form a graph, linked by references, each with the field's cardinality attached:
graph LR
Order["Order"] -->|"address [1,1]"| Address["Address"]
Order -->|"items [1,]"| LineItem["LineItem"]
good = Doc.from_oml('''
id: "A1"
status: "shipped"
total: 29.97
address: { street: "1 Main St"; city: "London" }
items: { sku: "W"; qty: 3; price: 9.99 }
''')
s.validate(good).okOutput
TrueAs a tree of labeled edges, this is the same document every format above would read into:
graph LR
order["(root)"] --> id["id: A1"]
order --> status["status: shipped"]
order --> total["total: 29.97"]
order --> address["address"]
address --> street["street: 1 Main St"]
address --> city["city: London"]
order --> items["items"]
items --> sku["sku: W"]
items --> qty["qty: 3"]
items --> price["price: 9.99"]
bad = Doc.from_oml('''
id: "A2"
status: "shipped"
total: "ten"
address: { street: "x"; city: "y" }
''')
print(s.validate(bad))Output
invalid:
at $.total: expected number, got string ('ten')
at $: field 'items' occurs 0 time(s), expected at least 1Two independent problems, both reported — a missing type match and a missing required field — since validation collects every failure rather than stopping at the first.