A serde-shaped serialization framework: one data model, many formats
Dependencies
///|
#dataderive(Serialize, Deserialize)
pub(all) struct User {
name : String
age : Int
}
// Works with every format, without the type mentioning any of them.
///|
let bytes = @json.to_bytes(user)
///|
let value = @data.to_value(user)moon add Yu-zh/dataimport {
"Yu-zh/data",
"Yu-zh/data/json",
}| Serialize | a type describing itself to any Serializer |
| Deserialize | a type reconstructing itself from any Deserializer |
| Serializer | a format receiving the data model |
| Deserializer | a format producing the data model |
| Value | the data model made concrete, for when a type is not known ahead of time |
///|
pub(open) trait Serialize {
fn[S : Serializer] serialize(Self, S) -> Unit raise SerError
}
///|
pub(open) trait Deserialize {
fn[D : Deserializer] deserialize(D) -> Self raise DeError
}///|
pub(all) struct User {
name : String
age : Int
/// An `Option` field: absent and explicitly null both read back as `None`.
nickname : String?
} derive(Eq, Debug)
///|
pub impl @data.Serialize for User with fn serialize(self, s) {
s.serialize_struct_begin("User", 3)
s.serialize_field("name", self.name)
s.serialize_field("age", self.age)
s.serialize_field("nickname", self.nickname)
s.serialize_struct_end()
}
///|
pub impl @data.Deserialize for User with fn deserialize(d) {
d.deserialize_struct_begin("User", ["name", "age", "nickname"])
let mut name = None
let mut age = None
let mut nickname = None
while d.deserialize_field_name() is Some(field) {
match field {
"name" => name = Some(d.deserialize_field_value())
"age" => age = Some(d.deserialize_field_value())
"nickname" => nickname = d.deserialize_field_value()
// Unknown fields are skipped rather than rejected.
_ => d.skip_value()
}
}
{
name: @data.required(name, "name", d.path()),
age: @data.required(age, "age", d.path()),
nickname,
}
}///|
test "one implementation, every format" {
let user = User::{ name: "ada", age: 36, nickname: None }
// JSON, as UTF-8 bytes.
inspect(
@json.to_string(user),
content="{\"name\":\"ada\",\"age\":36,\"nickname\":null}",
)
assert_eq((@json.from_string(@json.to_string(user)) : User), user)
// The same value in memory, with no encoding step.
inspect(
@data.to_value(user),
content="{\"name\": \"ada\", \"age\": 36, \"nickname\": null}",
)
assert_eq((@data.from_value(@data.to_value(user)) : User), user)
}///|
pub(all) enum Role {
Guest
Member(String)
Admin(level~ : Int, since~ : String)
} derive(Eq, Debug)
///|
pub impl @data.Serialize for Role with fn serialize(self, s) {
let name = "Role"
match self {
Guest =>
s.serialize_unit_variant({ enum_name: name, index: 0, name: "Guest" })
Member(team) =>
s.serialize_newtype_variant(
{ enum_name: name, index: 1, name: "Member" },
team,
)
Admin(level~, since~) => {
s.serialize_struct_variant_begin(
{ enum_name: name, index: 2, name: "Admin" },
2,
)
s.serialize_struct_variant_field("level", level)
s.serialize_struct_variant_field("since", since)
s.serialize_struct_variant_end()
}
}
}
///|
test "enums are externally tagged" {
inspect(@json.to_string(Guest), content="\"Guest\"")
inspect(@json.to_string(Member("core")), content="{\"Member\":\"core\"}")
inspect(
@json.to_string(Admin(level=2, since="2019")),
content="{\"Admin\":{\"level\":2,\"since\":\"2019\"}}",
)
}///|
#dataderive(Serialize, Deserialize)
pub(all) struct Config {
#datarename("max-retries")
max_retries : Int
#dataskip
cache_generation : Int
}moonx Yu-zh/data/derive [path ...]| Attribute | On | Effect |
|---|---|---|
| #data.derive(Serialize, Deserialize) | struct or enum | generates the named implementations |
| #data.rename("name") | field or variant | changes the name on the wire, not in MoonBit |
| #data.skip | field | never written; filled from Default when read |
///|
test "errors say where" {
let bad = "[{\"name\":\"ada\",\"age\":\"thirty-six\"}]"
try (@json.from_string(bad) : Array[User]) catch {
e =>
inspect(
e,
content="$[0].age: invalid type: expected a number, found string",
)
} noraise {
_ => fail("expected a type error")
}
}///|
test "Value carries any document" {
let text = "{\"tags\":[1,-2.5,true,null],\"meta\":{}}"
let value : @data.Value = @json.from_string(text)
guard value.get("tags") is Some(tags) else { fail("expected a tags field") }
inspect(tags, content="[1, -2.5, true, null]")
inspect(@json.to_string(value), content=text)
}///|
test "the transport is bytes" {
assert_eq(@json.to_bytes("é"), b"\x22\xc3\xa9\x22")
assert_eq((@json.from_bytes(b"[1,2]") : Array[Int]), [1, 2])
}impl Deserialize for Unitimpl Deserialize for Boolimpl Deserialize for Byteimpl Deserialize for Charimpl Deserialize for Intimpl Deserialize for Int16impl Deserialize for Int64impl Deserialize for UIntimpl Deserialize for UInt16impl Deserialize for UInt64impl Deserialize for Floatimpl Deserialize for Doubleimpl Deserialize for Stringimpl Deserialize for Option[T]impl Deserialize for FixedArray[T]impl Deserialize for Bytesimpl Deserialize for Array[T]impl Deserialize for Map[K, V]fn[K : Deserialize + Hash + Eq, V : Deserialize, D : Deserializer] deserialize(d : D) -> Map[K, V] raise DeErrorimpl Deserialize for Tuple2[A, B]impl Deserialize for Tuple3[A, B, C]fn[A : Deserialize, B : Deserialize, C : Deserialize, D : Deserializer] deserialize(d : D) -> (A, B, C) raise DeErrorpub(open) trait Deserializer {
fn path(Self) -> Path
fn deserialize_unit(Self) -> Unit raise DeError
fn deserialize_bool(Self) -> Bool raise DeError
fn deserialize_byte(Self) -> Byte raise DeError
fn deserialize_int16(Self) -> Int16 raise DeError
fn deserialize_uint16(Self) -> UInt16 raise DeError
fn deserialize_int(Self) -> Int raise DeError
fn deserialize_uint(Self) -> UInt raise DeError
fn deserialize_int64(Self) -> Int64 raise DeError
fn deserialize_uint64(Self) -> UInt64 raise DeError
fn deserialize_float(Self) -> Float raise DeError
fn deserialize_double(Self) -> Double raise DeError
fn deserialize_char(Self) -> Char raise DeError
fn deserialize_string(Self) -> String raise DeError
fn deserialize_bytes(Self) -> Bytes raise DeError
fn[T : Deserialize] deserialize_option(Self) -> T? raise DeError
fn deserialize_unit_struct(Self, String) -> Unit raise DeError = _
fn[T : Deserialize] deserialize_newtype_struct(Self, String) -> T raise DeError = _
fn deserialize_seq_begin(Self) -> Int? raise DeError
fn[T : Deserialize] deserialize_seq_next(Self) -> T? raise DeError
fn deserialize_tuple_begin(Self, Int) -> Unit raise DeError = _
fn[T : Deserialize] deserialize_tuple_next(Self) -> T raise DeError = _
fn deserialize_tuple_end(Self) -> Unit raise DeError = _
fn deserialize_tuple_struct_begin(Self, String, Int) -> Unit raise DeError = _
fn deserialize_map_begin(Self) -> Int? raise DeError
fn[K : Deserialize] deserialize_map_next_key(Self) -> K? raise DeError
fn[V : Deserialize] deserialize_map_value(Self) -> V raise DeError
fn deserialize_struct_begin(Self, String, Array[String]) -> Unit raise DeError
fn deserialize_field_name(Self) -> String? raise DeError
fn[T : Deserialize] deserialize_field_value(Self) -> T raise DeError
fn skip_value(Self) -> Unit raise DeError
fn deserialize_enum_begin(Self, String, Array[String]) -> String raise DeError
fn deserialize_unit_variant(Self) -> Unit raise DeError
fn[T : Deserialize] deserialize_newtype_variant(Self) -> T raise DeError
fn deserialize_enum_end(Self) -> Unit raise DeError = _
fn deserialize_any(Self) -> Value raise DeError
fn is_human_readable(Self) -> Bool = _
}impl Serialize for FixedArray[T]fn[A : Serialize, B : Serialize, S : Serializer] serialize(self : (A, B), s : S) -> Unit raise SerErrorpub(open) trait Serializer {
fn serialize_unit(Self) -> Unit raise SerError
fn serialize_bool(Self, Bool) -> Unit raise SerError
fn serialize_byte(Self, Byte) -> Unit raise SerError
fn serialize_int16(Self, Int16) -> Unit raise SerError
fn serialize_uint16(Self, UInt16) -> Unit raise SerError
fn serialize_int(Self, Int) -> Unit raise SerError
fn serialize_uint(Self, UInt) -> Unit raise SerError
fn serialize_int64(Self, Int64) -> Unit raise SerError
fn serialize_uint64(Self, UInt64) -> Unit raise SerError
fn serialize_float(Self, Float) -> Unit raise SerError
fn serialize_double(Self, Double) -> Unit raise SerError
fn serialize_char(Self, Char) -> Unit raise SerError
fn serialize_string(Self, String) -> Unit raise SerError
fn serialize_bytes(Self, Bytes) -> Unit raise SerError
fn serialize_none(Self) -> Unit raise SerError
fn[T : Serialize] serialize_some(Self, T) -> Unit raise SerError
fn serialize_unit_struct(Self, String) -> Unit raise SerError = _
fn[T : Serialize] serialize_newtype_struct(Self, String, T) -> Unit raise SerError = _
fn serialize_unit_variant(Self, VariantInfo) -> Unit raise SerError
fn[T : Serialize] serialize_newtype_variant(Self, VariantInfo, T) -> Unit raise SerError
fn serialize_seq_begin(Self, Int?) -> Unit raise SerError
fn[T : Serialize] serialize_seq_element(Self, T) -> Unit raise SerError
fn serialize_seq_end(Self) -> Unit raise SerError
fn serialize_tuple_begin(Self, Int) -> Unit raise SerError = _
fn[T : Serialize] serialize_tuple_element(Self, T) -> Unit raise SerError = _
fn serialize_tuple_end(Self) -> Unit raise SerError = _
fn serialize_tuple_struct_begin(Self, String, Int) -> Unit raise SerError = _
fn[T : Serialize] serialize_tuple_struct_field(Self, T) -> Unit raise SerError = _
fn serialize_tuple_struct_end(Self) -> Unit raise SerError = _
fn serialize_map_begin(Self, Int?) -> Unit raise SerError
fn[K : Serialize] serialize_map_key(Self, K) -> Unit raise SerError
fn[V : Serialize] serialize_map_value(Self, V) -> Unit raise SerError
fn serialize_map_end(Self) -> Unit raise SerError
fn serialize_struct_begin(Self, String, Int) -> Unit raise SerError
fn[T : Serialize] serialize_field(Self, String, T) -> Unit raise SerError
fn serialize_struct_end(Self) -> Unit raise SerError
fn serialize_tuple_variant_begin(Self, VariantInfo, Int) -> Unit raise SerError
fn[T : Serialize] serialize_tuple_variant_element(Self, T) -> Unit raise SerError = _
fn serialize_tuple_variant_end(Self) -> Unit raise SerError
fn serialize_struct_variant_begin(Self, VariantInfo, Int) -> Unit raise SerError
fn[T : Serialize] serialize_struct_variant_field(Self, String, T) -> Unit raise SerError = _
fn serialize_struct_variant_end(Self) -> Unit raise SerError
fn is_human_readable(Self) -> Bool = _
}pub(all) suberror DeError {
InvalidType(path~ : Path, expected~ : String, found~ : String)
InvalidValue(path~ : Path, expected~ : String, found~ : String)
InvalidLength(path~ : Path, expected~ : Int, found~ : Int)
MissingField(path~ : Path, field~ : String)
UnknownField(path~ : Path, field~ : String, expected~ : Array[String])
UnknownVariant(path~ : Path, variant~ : String, expected~ : Array[String])
Eof(path~ : Path)
DeCustom(path~ : Path, message~ : String)
}pub(all) suberror SerError {
UnsupportedType(format~ : String, kind~ : String)
SerCustom(message~ : String)
}impl Deserialize for Valueimpl Deserializer for ValueDeserializerfn deserialize_enum_begin(self : ValueDeserializer, _name : String, variants : Array[String]) -> String raise DeErrorfn deserialize_struct_begin(self : ValueDeserializer, _name : String, _fields : Array[String]) -> Unit raise DeErrorimpl Serializer for ValueSerializerfn[T : Serialize] serialize_field(self : ValueSerializer, name : String, v : T) -> Unit raise SerErrorfn[T : Serialize] serialize_newtype_variant(self : ValueSerializer, info : VariantInfo, v : T) -> Unit raise SerErrorfn serialize_struct_begin(self : ValueSerializer, _name : String, _len : Int) -> Unit raise SerErrorfn serialize_struct_variant_begin(self : ValueSerializer, info : VariantInfo, _len : Int) -> Unit raise SerErrorfn serialize_tuple_variant_begin(self : ValueSerializer, info : VariantInfo, len : Int) -> Unit raise SerErrorpub(all) struct VariantInfo {
enum_name : String
index : Int
name : String
}A serde-shaped serialization framework: one data model, many formats
Dependencies