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编码

Download history 295/week @ 2024-03-11 197/week @ 2024-03-18 303/week @ 2024-03-25 285/week @ 2024-04-01 162/week @ 2024-04-08 88/week @ 2024-04-15 16/week @ 2024-04-22 59/week @ 2024-04-29 257/week @ 2024-05-06 89/week @ 2024-05-13 61/week @ 2024-05-20 28/week @ 2024-06-10 16/week @ 2024-06-17 24/week @ 2024-06-24

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

MIT 许可证

30KB
853

msgpack-schema Crates.io docs.rs CI

msgpack-schema 是一种用于描述MessagePack编码数据格式的模式语言。它提供了两个 derive 宏 SerializeDeserialize,允许您以类型导向的方式将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());

编写自己的 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 中,应按上述方式许可,不附加任何其他条款或条件。

lib.rs:

MessagePack 数据模型

请参阅规范

依赖关系

~3MB
~58K SLoC