(how) does deserialization handle field names that are not valid Mojo identifiers - like “$version” in JSON schema?
@fieldwise_init
struct Foo:
var `$#^*(!@^&$%💩🔥﷽﷽`: String
fn main():
var f = Foo("aaaaa")
print(f.`$#^*(!@^&$%💩🔥﷽﷽`)
This isn’t really an issue for Mojo.
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 "********"
Version 0.3.2 is now available from the modular-community channel supporting mojo 1.0.0b1!