21个版本 (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日 |
#1093 in 编码
每月下载量:33
93KB
2K SLoC
msgpack-schema
msgpack-schema 是一种用于描述MessagePack编码数据格式的规范语言。它提供了两个 derive 宏 Serialize
和 Deserialize
,允许您以类型指导的方式将MessagePack二进制数据转换为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());
新类型结构体
只有一个元素的元组结构体会被透明处理。
#[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());
单元变体和空元组变体
单元变体和空元组变体被序列化为一个由标记值确定的单个fixint。
#[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());
编写自己的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的任何有意贡献均应按上述方式许可,而无需任何附加条款或条件。
依赖关系
~0.5–1.6MB
~31K SLoC