如何将 serde_json 与枚举之类的联合类型一起使用?
How to use serde_json with a union type like enum?
我有两个结构,我想 serialize/deserialize 将标记作为 JSON 中的 "type"
字段,就像这样。
#[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}
将 Thing
枚举上的 #[serde(tag = "type")]
更改为 #[serde(untagged)]
会导致相反的问题:Thing
实例正确序列化,但不再正确解析。
我的目标是让 JSON {"type": "ThingB", value: 0}
在反序列化期间计算为 Thing::ThingB(ThingB {value: 0})
,反之亦然,但前提是我要反序列化为 Thing
。如果我有一个展开的 ThingB
,比如 ThingB {value: 0}
,我希望它也序列化为 {"type": "ThingB", value: 0}
。
所以我的问题是:有没有办法分配 serde tag
或 untagged
属性,使它们仅在 serialization/deserialization 期间适用(类似于 serde 的 rename
)?如果没有,关于如何实施 Serialize
and/or Deserialize
以实现我的目标有什么建议吗?
您可以只在 Thing
枚举中使用 tag
,其他的保持干净:
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
。可以找到更多信息 here
我有两个结构,我想 serialize/deserialize 将标记作为 JSON 中的 "type"
字段,就像这样。
#[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}
将 Thing
枚举上的 #[serde(tag = "type")]
更改为 #[serde(untagged)]
会导致相反的问题:Thing
实例正确序列化,但不再正确解析。
我的目标是让 JSON {"type": "ThingB", value: 0}
在反序列化期间计算为 Thing::ThingB(ThingB {value: 0})
,反之亦然,但前提是我要反序列化为 Thing
。如果我有一个展开的 ThingB
,比如 ThingB {value: 0}
,我希望它也序列化为 {"type": "ThingB", value: 0}
。
所以我的问题是:有没有办法分配 serde tag
或 untagged
属性,使它们仅在 serialization/deserialization 期间适用(类似于 serde 的 rename
)?如果没有,关于如何实施 Serialize
and/or Deserialize
以实现我的目标有什么建议吗?
您可以只在 Thing
枚举中使用 tag
,其他的保持干净:
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
。可以找到更多信息 here