#serialization #schema #deserialize #messagepack #language #structures #msgpack-schema

msgpack-schema-impl

msgpack-schema crate 的实现细节

22 个版本 (3 个稳定版)

1.1.0 2023年7月16日
1.0.0 2023年6月21日
0.8.1 2023年6月18日
0.5.0 2023年3月18日
0.4.1 2021年7月29日

#46 in #messagepack

Download history 295/week @ 2024-03-11 200/week @ 2024-03-18 303/week @ 2024-03-25 285/week @ 2024-04-01 160/week @ 2024-04-08 87/week @ 2024-04-15 16/week @ 2024-04-22 66/week @ 2024-04-29 257/week @ 2024-05-06 89/week @ 2024-05-13 61/week @ 2024-05-20 2/week @ 2024-06-03 25/week @ 2024-06-10 18/week @ 2024-06-17 24/week @ 2024-06-24

每月 69 次下载
msgpack-schema 中使用

MIT 许可证

41KB
1K SLoC

msgpack-schema Crates.io docs.rs CI

msgpack-schema 是一种用于描述 MessagePack 编码数据格式的 schema 语言。它提供了两个 derive 宏 SerializeDeserialize,允许您以类型驱动的方式将 MessagePack 二进制数据转换为 Rust 数据结构,反之亦然。

use msgpack_schema::{Deserialize, Serialize};

#[derive(Deserialize, Serialize)]
struct Human {
    #[tag = 0]
    name: String,
    #[tag = 2]
    #[optional]
    age: Option<u32>,
}

rmp-serde 等其他 schema 语言相比,msgpack-schema 允许指定更紧凑的数据表示,例如,将 fixints 用作字段键,将 fixints 用作变体键等。

功能标志

  • proptest: 为 msgpack_value::Value 实现 proptest::arbitrary::Arbitrary

序列化和反序列化器的行为

具有命名字段的结构体

具有命名字段的结构体被序列化为一个 Map 对象,其中键是由 #[tag] 属性指定的 fixints。当前实现按顺序序列化字段,但不应依赖于此行为。

反序列化器将 Map 对象解释为创建此类结构体。字段顺序与结果无关。如果 Map 对象包含在结构体定义中未包含的额外键值对,反序列化器将简单地忽略它们。如果在一个 Map 对象中有两个或更多具有相同键的值,则最后一个值将覆盖前面的值。

#[derive(Serialize, Deserialize)]
struct S {
    #[tag = 0]
    x: u32,
    #[tag = 1]
    y: String,
}

let s = S {
  x: 42,
  y: "hello".to_owned(),
};

let b = b"\x82\x00\x2A\x01\xA5\x68\x65\x6c\x6c\x6f"; // 10 bytes; `{ 0: 42, 1: "hello" }`
assert_eq!(serialize(&s), b);
assert_eq!(s, deserialize(b).unwrap());

// ignores irrelevant key-value pairs
let b = b"\x83\x00\x2A\x02\xC3\x01\xA5\x68\x65\x6c\x6c\x6f"; // 12 bytes; `{ 0: 42, 2: true, 1: "hello" }`
assert_eq!(s, deserialize(b).unwrap());

// last value wins
let b = b"\x83\x00\xC3\x00\x2A\x01\xA5\x68\x65\x6c\x6c\x6f"; // 12 bytes; `{ 0: true, 0: 42, 1: "hello" }`
assert_eq!(s, deserialize(b).unwrap());

命名结构体中的字段可以使用#[optional]进行标记。

  • 标记的字段必须是类型为Option<T>
  • 在序列化过程中,如果字段数据包含None,则键值对不会包含在结果映射对象中。
  • 在反序列化过程中,如果提供的MsgPack映射对象中没有对应的键值对,则结果结构体的字段将被填充为None
#[derive(Serialize, Deserialize)]
struct S {
    #[tag = 0]
    x: u32,
    #[optional]
    #[tag = 1]
    y: Option<String>,
}

let s = S {
  x: 42,
  y: Some("hello".to_owned()),
};
let b = b"\x82\x00\x2A\x01\xA5\x68\x65\x6c\x6c\x6f"; // 10 bytes; `{ 0: 42, 1: "hello" }`
assert_eq!(serialize(&s), b);
assert_eq!(s, deserialize(b).unwrap());

let s = S {
  x: 42,
  y: None,
};
let b = b"\x81\x00\x2A"; // 3 bytes; `{ 0: 42 }`
assert_eq!(serialize(&s), b);
assert_eq!(s, deserialize(b).unwrap());

#[flatten]属性用于将单个命名结构体的定义分解为多个。

#[derive(Serialize)]
struct S1 {
    #[tag = 1]
    x: u32,
}

#[derive(Serialize)]
struct S2 {
    #[flatten]
    s1: S1,
    #[tag = 2]
    y: u32,
}

#[derive(Serialize)]
struct S3 {
    #[tag = 1]
    x: u32,
    #[tag = 2]
    y: u32,
}

assert_eq!(serialize(S2 { s1: S1 { x: 42 }, y: 43, }), serialize(S3 { x: 42, y: 43 }));

具有命名字段的结构体可以附加#[untagged]。无标记结构体将被序列化为一个数组,并且不会包含标记。

#[derive(Serialize, Deserialize)]
#[untagged]
struct S {
    x: u32,
    y: String,
}

let s = S {
  x: 42,
  y: "hello".to_owned(),
};
let b = b"\x92\x2A\xA5\x68\x65\x6c\x6c\x6f"; // 8 bytes; `[ 42, "hello" ]`

assert_eq!(serialize(&s), b);
assert_eq!(s, deserialize(b).unwrap());

新类型结构体

只有一个元素的元组结构体被透明处理。

#[derive(Serialize, Deserialize)]
struct S(u32);

let s = S(42);
let b = b"\x2A"; // 1 byte; `42`

assert_eq!(serialize(&s), b);
assert_eq!(s, deserialize(b).unwrap());

单元结构体和空元组结构体

单元结构体和空元组结构体的序列化和反序列化有意识地不支持。

// It is error to derive `Serialize` / `Deserialize` for these types of structs.
struct S1;
struct S2();

元组结构体

包含多个元素的元组结构体将被编码为数组。如果反序列化数组的长度不匹配,则是一个验证错误。

#[derive(Serialize, Deserialize)]
struct S(u32, bool);

let s = S(42, true);
let b = b"\x92\x2A\xC3"; // 3 bytes; `[ 42, true ]`

assert_eq!(serialize(&s), b);
assert_eq!(s, deserialize(b).unwrap());

单元变体和空元组变体

单元变体和空元组变体将被序列化为一个单值固定整数,其值由标记决定。

#[derive(Serialize, Deserialize)]
enum E {
    #[tag = 3]
    Foo
}

let e = E::Foo;
let b = b"\x03"; // 1 byte; `3`

assert_eq!(serialize(&e), b);
assert_eq!(e, deserialize(b).unwrap());
#[derive(Serialize, Deserialize)]
enum E {
    #[tag = 3]
    Foo()
}

let e = E::Foo();
let b = b"\x03"; // 1 byte; `3`

assert_eq!(serialize(&e), b);
assert_eq!(e, deserialize(b).unwrap());

新类型变体

新类型变体(单元素元组变体)将被序列化为一个标记和内部值的数组。

#[derive(Serialize, Deserialize)]
enum E {
    #[tag = 3]
    Foo(u32)
}

let e = E::Foo(42);
let b = b"\x92\x03\x2A"; // 3 bytes; `[ 3, 42 ]`

assert_eq!(serialize(&e), b);
assert_eq!(e, deserialize(b).unwrap());

无标记变体

当所有变体都是新类型变体时,枚举可以附加#[untagged]。序列化无标记变体会产生与内部类型相同的数据布局。反序列化器将逐个尝试从第一个变体到最后一个变体进行反序列化,以将无标记枚举类型反序列化。

#[derive(Serialize, Deserialize)]
#[untagged]
enum E {
    Foo(String),
    Bar(u32),
}

let e = E::Bar(42);
let b = b"\x2A"; // 1 byte; `42`

assert_eq!(serialize(&e), b);
assert_eq!(e, deserialize(b).unwrap());

编写自己的SerializeDeserialize实现

在以下情况下,您可能需要编写自己的SerializeDeserialize实现:

  1. 您需要为已经由其他人定义的类型实现impl
  2. 您需要极端的效率。
  3. 两者都需要。

IpAddr是这样的类型,满足(3)。在最有效的情况下,我们希望它在任何时候都是4或16字节长度加上一个标记字节。这通过以下硬编码实现实现。

struct IpAddr(pub std::net::IpAddr);

impl Serialize for IpAddr {
    fn serialize(&self, serializer: &mut Serializer) {
        match self.0 {
            std::net::IpAddr::V4(v4) => {
                serializer.serialize_str(&v4.octets()); // 5 bytes
            }
            std::net::IpAddr::V6(v6) => {
                serializer.serialize_str(&v6.octets()); // 17 bytes
            }
        }
    }
}

impl Deserialize for IpAddr {
    fn deserialize(deserializer: &mut Deserializer) -> Result<Self, DeserializeError> {
        let Str(data) = deserializer.deserialize()?;
        let ipaddr = match data.len() {
            4 => std::net::IpAddr::V4(std::net::Ipv4Addr::from(
                <[u8; 4]>::try_from(data).unwrap(),
            )),
            16 => std::net::IpAddr::V6(std::net::Ipv6Addr::from(
                <[u8; 16]>::try_from(data).unwrap(),
            )),
            _ => return Err(ValidationError.into()),
        };
        Ok(Self(ipaddr))
    }
}

附录:速查表

模式 Rust MessagePack(可读性人类)
struct S {
    #[tag = 0]
    x: u32,
    #[tag = 1]
    y: bool,
}
S{x: 42,y: true } { 0: 42, 1: true }
struct S {
    #[optional]
    #[tag = 0]
    x: Option<u32>,
}
S{x: Some(42) } { 0: 42 }
struct S {
    #[optional]
    #[tag = 0]
    x: Option<u32>,
}
S{x: None } {}
#[untagged]
struct S {
    #[tag = 0]
    x: u32,
    #[tag = 1]
    y: bool,
}
S{x: 42,y: true } [ 42, true ]
struct S(u32) S(42) 42
struct S S UNSUPPORTED
struct S() S() UNSUPPORTED
struct S(u32, bool) S(42, true) [ 42, true ]
enum E {
    #[tag = 3]
    Foo
}
E::Foo 3
enum E {
    #[tag = 3]
    Foo()
}
E::Foo() 3
enum E {
    #[tag = 3]
    Foo(u32)
}
E::Foo(42) [ 3, 42 ]
#[untagged]
enum E {
    Foo(u32)
    Bar(bool)
}
E::Bar(true) true

许可证

MIT许可证下许可。
除非您明确声明,否则您提交给msgpack-schema的任何贡献都将按照上述方式许可,而无需任何额外的条款或条件。

依赖项

~315–770KB
~19K SLoC