2

"type"我有两个结构,我想用标签作为JSON中的字段来序列化/反序列化,就像这样。

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type")]
struct ThingA {
    value: usize,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type")]
struct ThingB {
    value: usize,
}

这些按预期序列化。例如,

let a = ThingA { value: 0 };
println!("{}", serde_json::to_string(&a)?);
// This yields the expected result:
// {"type":"ThingA","value":0}

但是,当我尝试添加一个枚举作为结构的联合类型时,我遇到了麻烦。

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type")]
enum Thing {
    ThingA(ThingA),
    ThingB(ThingB),
}

上面的定义适用于反序列化 JSON,但在序列化期间添加了一个额外的字段。

let json = r#"{"type": "ThingB", "value": 0}"#;
let thing: Thing = serde_json::from_str(json)?;
// Correctly parses to:
// ThingB(ThingB { value: 0 })

println!("{}", serde_json::to_string(&thing)?);
// Incorrectly serializes with an extra "type" field:
// {"type":"ThingB","type":"ThingB","value":0}

更改#[serde(tag = "type")]#[serde(untagged)]onThing枚举会导致相反的问题:Thing实例正确序列化,但不再正确解析。

我的目标是让 JSON在反序列化期间{"type": "ThingB", value: 0}进行评估Thing::ThingB(ThingB {value: 0}),反之亦然,但前提是我要反序列化为Thing. 如果我有一个 unwrapped 的ThingB, like ThingB {value: 0},我希望它也可以序列化{"type": "ThingB", value: 0}

所以我的问题是:有没有办法分配 serdetaguntagged属性,以便它们仅在序列化/反序列化期间应用(类似于 serde's rename)?如果没有,关于如何实施Serialize和/或Deserialize实现我的目标的任何建议?

4

1 回答 1

2

您可以只tag在您的Thing枚举中使用,而让其他人保持清洁:

use serde::{Serialize, Deserialize}; // 1.0.124
use serde_json; // 1.0.64

#[derive(Debug, Clone, Serialize, Deserialize)]
struct ThingA {
    value: usize,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
struct ThingB {
    value: usize,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type")]
enum Thing {
    ThingA(ThingA),
    ThingB(ThingB),
}

fn main() {
    let json = r#"{"type": "ThingB", "value": 0}"#;
    let thing: Thing = serde_json::from_str(json).unwrap();
    println!("{}", serde_json::to_string(&thing).unwrap());

}

操场

按照评论中的要求。如果我们想要同时标记(枚举和结构),我们需要serde使用包装器和with. 更多信息可以在这里找到

于 2021-04-06T08:08:25.700 回答