【问题标题】:Is there a way to define a tag field with Serde?有没有办法用 Serde 定义标签字段?
【发布时间】:2020-04-13 14:45:21
【问题描述】:

我想要这样的东西:

#[derive(Debug, Serialize, Deserialize)]
struct MyStruct {
    field1: String,
    field2: Option<u64>,
    #[serde(tag(value = "tag_value"))]
    tag: ()
}

#[serde(tag(value = "tag_value"))] 不是 Serde 提供的实际属性,它在这里只是为了表达一个想法。我知道我可以自己完成所有的序列化,使用远程,(de)serialize_with 等,但是这些需要大量的样板代码。

思路是标签字段必须存在,MyStruct序列化为JSON应该是:

{
    "field1": "foo",
    "field2": 42,
    "tag": "tag_value"
}

如果“tag”字段丢失或映射到与“tag_value”不同的值,则反序列化必须失败。

【问题讨论】:

  • 您能更好地解释一下您所说的“假”字段是什么意思吗?那该怎么办?它适用于序列化,或反序列化,或两者兼而有之?您是否尝试过创建这样的样板?
  • 听起来您正在尝试重新实现枚举。具体来说,您有类似 internally-tagged enum 的东西。
  • @Shepmaster 不完全是。枚举不能从模块外部扩展,当所有可能的变体混合在一起时,我似乎很难提取有用的错误。

标签: rust serde


【解决方案1】:

使用单个变体枚举:

use serde; // 1.0.104
use serde_json; // 1.0.48

#[derive(Debug, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "snake_case")]
enum Tag {
    TagValue,
}

#[derive(Debug, serde::Serialize, serde::Deserialize)]
struct MyStruct {
    field1: String,
    field2: Option<u64>,
    tag: Tag,
}

fn main() {
    let s = MyStruct {
        field1: "foo".to_string(),
        field2: Some(42),
        tag: Tag::TagValue,
    };

    // The tag is included when serializing
    println!("{:?}", serde_json::to_string(&s));

    // Tag is required when deserializing
    println!(
        "{:?}",
        serde_json::from_str::<MyStruct>(
            "{\"field1\":\"foo\",\"field2\":42,\"tag\":\"tag_value\"}"
        )
    );
    println!(
        "{:?}",
        serde_json::from_str::<MyStruct>("{\"field1\":\"foo\",\"field2\":42}")
    );

    // A bad tag fails
    println!(
        "{:?}",
        serde_json::from_str::<MyStruct>("{\"field1\":\"foo\",\"field2\":42,\"tag\":\"oops\"}")
    );
}

打印出来

Ok("{\"field1\":\"foo\",\"field2\":42,\"tag\":\"tag_value\"}")
Ok(MyStruct { field1: "foo", field2: Some(42), tag: TagValue })
Err(Error("missing field `tag`", line: 1, column: 28))
Err(Error("unknown variant `oops`, expected `tag_value`", line: 1, column: 40))

(Permalink to the playground)

【讨论】:

  • 值得指出的是,单变量枚举不占用空间,因此不会产生过多的运行时成本。
【解决方案2】:

对于只有一个标签的有限情况,您可以在结构本身上使用#[serde(tag)]

use serde::Serialize; // 1.0.114
use serde_json; // 1.0.56

#[derive(Debug, Serialize)]
#[serde(tag = "tag", rename = "tag_value")]
struct MyStruct {
    field1: String,
    field2: Option<u64>,
}

fn main() {
    let s = MyStruct {
        field1: "hello".into(),
        field2: None,
    };
    println!("{}", serde_json::to_string(&s).unwrap());
}
{"tag":"tag_value","field1":"hello","field2":null}

【讨论】:

    猜你喜欢
    • 2017-06-03
    • 1970-01-01
    • 1970-01-01
    • 2018-05-03
    • 1970-01-01
    • 2010-09-11
    • 2017-03-26
    • 2021-06-04
    • 1970-01-01
    相关资源
    最近更新 更多