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
每月 69 次下载
在 msgpack-schema 中使用
41KB
1K SLoC
msgpack-schema
msgpack-schema 是一种用于描述 MessagePack 编码数据格式的 schema 语言。它提供了两个 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
等其他 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());
编写自己的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的任何贡献都将按照上述方式许可,而无需任何额外的条款或条件。
依赖项
~315–770KB
~19K SLoC