#serialization #data #serde #syntax #enums #notation #tuple

rudano

尽可能接近 Rust 自身语法的数据序列化格式

2 个版本

0.1.1 2022 年 12 月 16 日
0.1.0 2020 年 8 月 17 日

#892 in 编码

32 每月下载次数

MIT/Apache

130KB
3.5K SLoC

Rudano

Build Status Crate API Minimum rustc version

Rudano,Rust 数据表示法,是一种旨在尽可能接近 Rust 自身语法的数据序列化格式。

为什么?

虽然我非常喜欢 RON,但我发现语法差异在频繁在它和 Rust 之间切换上下文时会使您感到困惑,特别是当 () 用于结构体时。

这就是我创建鲁达诺的原因,重点是使其与 Rust 的字面量表示法本身非常相似,这样您就可以使用您已经熟悉的相同语法来编写您的“对象”描述。

例外情况是映射,Rust 中没有字面量语法,并且选择使用 [] 来分隔。在 其规范 中解释了原因。

示例和比较

考虑以下虚构的 Rust 结构

#[derive(Serialize, Deserialize)]
enum Ip {
    V4(u8, u8, u8, u8),
    V6(u8, u8, u8, u8, u8, u8, u8, u8),
}

#[derive(Serialize, Deserialize)]
struct Network {
    name: String,
    local_address: Ip,
    hosts: HashMap<String, Ip>,
}

在鲁达诺中此结构的示例

// Struct name is required (see why below)
Network {
    name: "Local Network",
    // Enums are supported
    local_address: V4(192, 168, 0, 100),
    hosts: [
        "Foo": V6(0, 0, 0, 0, 0, 0, 0, 0xA3),
        "Bar": V4(192, 168, 0, 104),
    ], // Trailing comma is allowed
}

相同的 Ron 示例

// Struct name is optional
Network (
    name: "Local Network",
    // Enums are supported
    local_address: V4(192, 168, 0, 100),
    hosts: {
        "Foo": V6(0, 0, 0, 0, 0, 0, 0, 0xA3),
        "Bar": V4(192, 168, 0, 104),
    }, // Trailing comma is allowed
)

在 JSON 中,不允许注释,但我希望它不言自明

{
    "name": "Local Network",
    "local_address": {
        "V4": [192, 168, 0, 100]
    },
    "hosts": {
        "Foo": {
            "V6": [0, 0, 0, 0, 0, 0, 0, 0xA3]
        },
        "Bar": {
            "V4": [192, 168, 0, 104]
        }
    }
}
Rudano RON JSON
注释 // // 和 /* */ 不允许
结构体 Name { property: value } Name ( property: value ) { "property": value }
数组 [value] [value] [value]
映射 [key: value] { key: value } { key: value }
枚举 Variant(data) Variant(data) { "Variant": data }
尾部逗号 允许 允许 不允许

贡献

欢迎提出任何问题、错误报告、建议或拉取请求!只需打开一个问题或 PR。

稳定性

不稳定,该格式本身可能需要更改以解决歧义问题并更好地符合 Rust 的语法,使用 semver 进行破坏性更改。

注意事项

  • 使用借用字符串进行零拷贝反序列化时,如果使用转义码,将失败。(我相信这同样适用于任何其他格式)

为什么需要结构体名称?

由于格式中需要结构体名称,因此可以在不破坏向后兼容性的情况下将结构体转换为枚举变体。例如,当使用

struct Point { position: (i32, i32), name: String }

enum Object {
    Point { position: (i32, i32), name: String },
    Canvas { size: (i32, i32) }
}

序列化值 Point 如下所示:Point { position: (10, 20), "Great Point!" } 既可以作为 Point 类型也可以作为 Object 类型。这对于所有结构体类型及其等效枚举变体(Unit, NewType, Tuple 和 Struct)都有效。

Rust 版本支持

最低支持版本(MSRV)为 1.40 或更高,并且已使用 1.40.0(稳定版)、beta 版和 nightly 版本进行测试。仅在对小版本(0.x)或更高版本进行更新时才会更改。

数据类型

注意,在所有带有定义的示例中,为了简洁,省略了 #[derive(Serialize, Deserialize)]

布尔类型

布尔类型可以是字面量 truefalse

示例

  • true
  • false

有符号整数

有符号整数包括 i8, i16, i32, i64 和 i128。如果是负数,则开头为 -,如果是正数,则没有 +。数字中的下划线 _ 被忽略,并且可以使用前缀 0x0o0b 分别以十六进制、八进制或二进制形式书写。

示例

对于 i16

  • 27
  • 43
  • 0x2A
  • -0x1F
  • 0b11010
  • -37
  • -32768
  • 32767

无符号整数

无符号整数包括 u8, u16, u32, u64 和 u128。它们不支持 -+。数字中的下划线 _ 被忽略,并且可以使用前缀 0x0o0b 分别以十六进制、八进制或二进制形式书写。

示例

对于 u16

  • 27
  • 43
  • 0x2A
  • 0b11010
  • 0
  • 65535

浮点数

浮点数类型包括 f32 和 f64,目前尚未完全指定。

示例

对于 f32

  • 27
  • 43.0
  • 239.34
  • -37.3
  • -37.0E+12
  • -92
  • inf
  • -inf

字符

字符类型由 '' 包围,并可以包含 参考中定义的任何字符或转义码

示例

  • 'a'
  • '@'
  • '\x5A'
  • '\u{bf0}'
  • '\u{1f638}'
  • '😸'
  • '\n'
  • '\t'
  • '\u{10ffff}'
  • '\u{fffd}'

字符串

字符串类型由 "" 包围,并可以包含 参考中定义的任何字符或转义码

示例

  • "a"
  • "@"
  • "The quick brown fox jumps over the lazy dog"
  • "\x5A"
  • "\u{bf0}\u{1f638}"
  • "😸"
  • "such\nwow"
  • "very\tlol"
  • "\u{10ffff}"
  • "\u{fffd}"

选项

选项类型有两种可能性:它可能是标识符 None 或包含括号和值的标识符 Some

示例

  • None
  • Some(())
  • Some(30)

单元类型

单元类型只包含一个可能的值: ()

示例

  • ()

单元结构

单元结构是一个命名值,其唯一值为结构体本身。它以结构体名称表示,可选地使用空花括号({})。

示例

定义

struct 单元类型;

序列化

  • 单元类型
  • 单元类型{}

新类型结构

新类型结构是具有单个匿名字段的结构体。它通过结构体名称、括号和值表示。实际上,它是元组结构体的一个特例。如果想要它只表示其内部值,请使用#[serde(transparent)]

示例

定义

struct Newtype(u8);

序列化

  • Newtype(39)
  • Newtype(0xa8)

定义

#[serde(transparent)]
struct Transparent(String);

序列化

  • ""
  • "它工作了!"

元组结构

元组结构体是具有多个匿名字段的结构体,类似于命名元组。它通过结构体名称和括号内的值表示。

示例

定义

struct TupleStruct(u8, char);

序列化

  • TupleStruct(10, 'a')

结构体

结构体是具有命名字段的类型。表示为结构体名称,后跟花括号和逗号分隔的 标识符: 对。允许使用尾随逗号(并且对于多行是推荐的)。

示例

定义

struct Point { position: (i32, i32), name: String }

序列化

  • {位置: (10, 20), "太棒了!" }

枚举变体

枚举变体与它们的结构体对应项以完全相同的方式进行序列化和反序列化。变体类型包括:单元、新类型、元组和结构体。

示例

定义

enum Enum {
    Unit,
    Newtype(u32),
    Tuple(u32, u32),
    Struct {
        a: u32,
    },
}

struct Variants {
    unit: Enum,
    newtype: Enum,
    tuple: Enum,
    struct_variant: Enum,
}

具有所有变体的序列化结构体

Variants {
    unit: Unit,
    newtype: Newtype(70),
    tuple: Tuple(20, 80),
    struct_variant: Struct {
        a: 10,
    },
}

序列(或数组)

数组是相同类型元素的连续序列。通过花括号内的逗号分隔的元素列表表示。由 serde 自动派生为 Vec,实际上可以通过在字段定义中添加 #[serde(serialize_with = "rudano::serialize_array")] 来用于数组。

示例

  • [10, 20, 30]
  • ["abc", "wow"]

映射

映射是一组(键,值)对。表示为逗号分隔的序列,每个对在键和值之间都有一个冒号(:)。由 serde 自动派生为 HashMap 和其他 std 映射类型。

示例

定义

HashMap<字符串, i32>

序列化

  • ["x": 10, "y": 20]
  • ["蓝色": 21, "红色": 22]

定义

HashMap<i32, 字符串>

  • [1: "一个"]
  • [13: "太高了"]

理由

在 rudano 中,映射使用 [] 分隔。原因在于映射是同质结构(类似于数组,但与结构体不同),并且实际上可以在 Rust 代码中使用 [(key0, value0), (key1, value1)].collect() 初始化,这与 RuDaNo 的 [key0: value0, key1: value1] 类似。这也使得映射与结构体不同,这是 RON 的原始动机之一。

Rust 语法差异

这些差异是故意的(但可能需要讨论)

  • 允许没有小数点的浮点类型
  • NaNinf-inf 以这样的方式表示(这是 rustc 打印它们的方式),即使它们不是有效的字面量。
  • 不支持具有类型后缀的数字(例如 15u64)
  • 向量序列化为 [1, 2, 3],而不是 vec![1, 2, 3]
  • 数组默认按元组序列化(因为这是默认的序列化实现)。使用 #[serde(serialize_with = "rudano::serialize_array")] 将使其使用数组表示法(作为 [] 序列)进行序列化

许可证

此软件包(库)根据MIT许可证或Apache许可证(版本2.0)的条款进行分发,由您选择。请参阅 LICENSE-MITLICENSE-APACHE 了解详细信息。

依赖项

~260–580KB
~12K SLoC