EmberJson 0.3.1
EmberJson 0.3.1 (Mojo 0.26.2) is now available from the Mojo community pixi channel.
Whats new
Data validation features in structured parsing
The schema module now includes a selection of different utility types to add additional power
to the existing reflection based structured parsing features.
Validators
| Validator | Description | Example |
|---|---|---|
Range[T, min, max] |
Inclusive range (min <= value <= max) |
Range[Int, 0, 100] |
ExclusiveRange[T, min, max] |
Exclusive range (min < value < max) |
ExclusiveRange[Float64, 0.0, 1.0] |
Size[T, min, max] |
Length/size constraint | Size[String, 1, 255] |
NonEmpty[T] |
Non-empty check | NonEmpty[List[Int]] |
StartsWith[prefix] |
String prefix check | StartsWith["https://"] |
EndsWith[suffix] |
String suffix check | EndsWith[".json"] |
Eq[value] |
Equality check | Eq[42] |
Ne[value] |
Inequality check | Ne["forbidden"] |
MultipleOf[base] |
Divisibility check | MultipleOf[Int64(10)] |
Unique[T] |
All elements unique | Unique[List[Int]] |
Enum[T, *values] |
Set membership | Enum[String, "red", "green", "blue"] |
Combine validators for complex constraints:
from emberjson import *
# AllOf: ALL validators must pass
var v = deserialize[
AllOf[String, Size[String, 3, 7], StartsWith["a"]]
]('"astring"')
# OneOf: EXACTLY one validator must pass
var o = deserialize[
OneOf[String, Eq["red"], Eq["green"], Eq["blue"]]
]('"red"')
# AnyOf: AT LEAST one validator must pass
var a = deserialize[
AnyOf[Int, Eq[1], Eq[2], Range[Int, 10, 20]]
]("15")
# NoneOf: NO validators must pass
var n = deserialize[
NoneOf[Int, Range[Int, 0, 5], Eq[100]]
]("7")
# Not: invert any validator
var x = deserialize[Not[Int, Range[Int, 0, 10]]]("15")
Data Transformers
Transformers modify values during deserialization or serialization:
from emberjson import *
# Default: use a fallback value when the field is missing or null
var d = deserialize[Default[Int, 42]]("null")
print(d[]) # prints 42
# Secret: deserializes normally, serializes as "********"
var pw = deserialize[Secret[String]]('"my_password"')
print(pw[]) # prints my_password
print(serialize(pw)) # prints "********"
# Clamp: constrains value to a range instead of rejecting
var c = deserialize[Clamp[Int, 0, 100]]("150")
print(c[]) # prints 100 (clamped to max)
# CoerceInt/CoerceFloat/CoerceString: type coercion from JSON
var i = deserialize[CoerceInt]('"123"')
print(i[]) # prints 123 (coerced from string)
# Transform: apply a function during deserialization
def date_to_epoch(s: String) -> Int:
if s == "2024-01-01":
return 1704067200
return 0
var epoch = deserialize[Transform[String, Int, date_to_epoch]]('"2024-01-01"')
print(epoch[]) # prints 1704067200
Cross-Field Validation
Validate relationships between fields of a struct:
from emberjson import *
from emberjson.schema import CrossFieldValidator
@fieldwise_init
struct DateRange(Defaultable, Movable):
var start: Int
var end: Int
def __init__(out self):
self.start = 0
self.end = 0
def validate_order(start: Int, end: Int) raises:
if start >= end:
raise Error("start must be before end")
def main() raises:
var dr = deserialize[
CrossFieldValidator[DateRange, "start", "end", validate_order]
]('{"start": 1, "end": 10}')
print(dr[].start) # prints 1
print(dr[].end) # prints 10
Using Validators in Structs
from emberjson import *
@fieldwise_init
struct Config(Defaultable, Movable):
var name: NonEmpty[String]
var port: Range[Int, 1, 65535]
var timeout: Default[Int, 30]
var password: Secret[String]
def __init__(out self):
self.name = "default"
self.port = 80
self.timeout = Default[Int, 30]()
self.password = ""
def main() raises:
var cfg = deserialize[Config](
'{"name": "myapp", "port": 8080, "password": "s3cret"}'
)
print(cfg.name[]) # prints myapp
print(cfg.port[]) # prints 8080
print(cfg.timeout[]) # prints 30 (default, since missing from JSON)
print(serialize(cfg)) # password serialized as "********"