9个版本 (3个稳定版)
1.1.0 | 2023年7月16日 |
---|---|
1.0.1 | 2023年7月3日 |
1.0.0 | 2023年6月21日 |
0.8.1 | 2023年6月18日 |
0.4.2 | 2023年3月11日 |
#1734 在 编码
每月68次 下载
在 msgpack-schema 中使用
30KB
853 行
msgpack-schema
msgpack-schema 是一种用于描述MessagePack编码数据格式的模式语言。它提供了两个 derive 宏 Serialize
和 Deserialize
,允许您以类型导向的方式将MessagePack二进制数据转换为Rust数据结构或从Rust数据结构转换。
use msgpack_schema::{Deserialize, Serialize};
#[derive(Deserialize, Serialize)]
struct Human {
#[tag = 0]
name: String,
#[tag = 2]
#[optional]
age: Option<u32>,
}
与像 rmp-serde
这样的其他模式语言相比,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());
Newtype 结构体
只有一个元素的元组结构体被透明处理。
#[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());
Newtype 变体
Newtype 变体(一个元素的元组变体)将序列化为标签和内部值的数组。
#[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]
,当所有变体都是 Newtype 变体时。序列化未标记变体会导致与内部类型相同的数据布局。反序列化器通过尝试从第一个变体到最后的变体进行反序列化,将未标记枚举类型反序列化为未标记枚举类型。
#[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());
编写自己的 Serialize
和 Deserialize
实现
在以下情况下,您可能希望编写自己的 Serialize
和 Deserialize
实现
- 您需要为已由他人定义的类型编写
impl
。 - 您需要极端的效率。
- 两者都需要。
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(可读性) |
---|---|---|
|
S{x: 42,y: true }
|
{ 0: 42, 1: true }
|
|
S{x: Some(42) }
|
{ 0: 42 }
|
|
S{x: None }
|
{}
|
|
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 ]
|
|
E::Foo
|
3
|
|
E::Foo()
|
3
|
|
E::Foo(42)
|
[ 3, 42 ]
|
|
E::Bar(true)
|
true
|
许可
许可在 MIT 许可证 下。除非您明确声明,否则您提交的任何贡献,有意包含在 msgpack-schema 中,应按上述方式许可,不附加任何其他条款或条件。
lib.rs
:
MessagePack 数据模型
请参阅规范。
依赖关系
~3MB
~58K SLoC