如何有条件地将 JSON 反序列化为枚举的两个不同变体?
How to conditionally deserialize JSON to two different variants of an enum?
假设我有 JSON 如下数据:
{
"type": "A",
"value": [ 1, 2, 3, 4, 5 ]
}
{
"type": "B",
"value": [ [ 1, 2, 3, 4, 5 ], [ 6, 7, 8 ] ]
}
type
决定 value
的类型,在第一个例子中是 Vec<u32>
,在第二个例子中是 Vec<Vec<u32>>
.
如果我将以上数据表示如下:
enum DataValue {
TypeA(Vec<u32>),
TypeB(Vec<Vec<u32>>)
}
struct Data {
data_type: String,
value: DataValue
}
如何实现 serde 反序列化以正确解码这些值?
这可能是个人意见,但我通常会尽量避免对枚举进行序列化/反序列化。
这在 C++ 中不是很相似吗?
struct DataValue final{
TypeA(Vec<u32>) final,
TypeB(Vec<Vec<u32>>) final
}
幸运的是 serde
内置支持 enum type:
//# serde = { version = "1.0.99", features = ["derive"] }
//# serde_json = "1.0.40"
use serde::Deserialize;
#[derive(Deserialize, Debug)]
#[serde(tag = "type")]
enum Data {
A { value: Vec<u32> },
B { value: Vec<Vec<u32>> },
}
fn main() {
let a: Data = serde_json::from_str(r#"{"type": "A", "value": [ 1, 2, 3, 4, 5 ]}"#).unwrap();
let b: Data =
serde_json::from_str(r#"{"type": "B", "value": [[1, 2, 3, 4, 5], [6, 7, 8 ]]}"#).unwrap();
println!("{:?}", a);
println!("{:?}", b);
}
您可以将 JSON 数据直接反序列化为 DataValue
的实例,前提是您向 Serde 提供足够的信息以了解如何执行此操作:
#[derive(Debug, Deserialize)]
#[serde(tag = "type", content = "value")]
enum DataValue {
#[serde(rename = "A")]
TypeA(Vec<u32>),
#[serde(rename = "B")]
TypeB(Vec<Vec<u32>>),
}
let data_a = r#"
{
"type": "A",
"value": [1, 2, 3, 4, 5]
}"#;
let a: DataValue = serde_json::from_str(data_a)?;
如果将枚举变体命名为 A
和 B
,则可以省略 #[serde(rename = "…")]
属性。
这种序列化枚举的方式被称为"adjacent tagging"。您可以在 Serde documentation on enum serialization.
中了解标记枚举的各种选项
您的 Data
结构包含多余的附加标签 data_type
。此信息已编码在枚举中,因此我认为您不需要此信息。如果你需要这个信息作为一个字符串,你可以在枚举中添加一个方法:
impl DataValue {
fn variant_name(&self) -> &'static str {
match self {
DataValue::TypeA(_) => "A",
DataValue::TypeB(_) => "B",
}
}
}
假设我有 JSON 如下数据:
{
"type": "A",
"value": [ 1, 2, 3, 4, 5 ]
}
{
"type": "B",
"value": [ [ 1, 2, 3, 4, 5 ], [ 6, 7, 8 ] ]
}
type
决定 value
的类型,在第一个例子中是 Vec<u32>
,在第二个例子中是 Vec<Vec<u32>>
.
如果我将以上数据表示如下:
enum DataValue {
TypeA(Vec<u32>),
TypeB(Vec<Vec<u32>>)
}
struct Data {
data_type: String,
value: DataValue
}
如何实现 serde 反序列化以正确解码这些值?
这可能是个人意见,但我通常会尽量避免对枚举进行序列化/反序列化。
这在 C++ 中不是很相似吗?
struct DataValue final{
TypeA(Vec<u32>) final,
TypeB(Vec<Vec<u32>>) final
}
幸运的是 serde
内置支持 enum type:
//# serde = { version = "1.0.99", features = ["derive"] }
//# serde_json = "1.0.40"
use serde::Deserialize;
#[derive(Deserialize, Debug)]
#[serde(tag = "type")]
enum Data {
A { value: Vec<u32> },
B { value: Vec<Vec<u32>> },
}
fn main() {
let a: Data = serde_json::from_str(r#"{"type": "A", "value": [ 1, 2, 3, 4, 5 ]}"#).unwrap();
let b: Data =
serde_json::from_str(r#"{"type": "B", "value": [[1, 2, 3, 4, 5], [6, 7, 8 ]]}"#).unwrap();
println!("{:?}", a);
println!("{:?}", b);
}
您可以将 JSON 数据直接反序列化为 DataValue
的实例,前提是您向 Serde 提供足够的信息以了解如何执行此操作:
#[derive(Debug, Deserialize)]
#[serde(tag = "type", content = "value")]
enum DataValue {
#[serde(rename = "A")]
TypeA(Vec<u32>),
#[serde(rename = "B")]
TypeB(Vec<Vec<u32>>),
}
let data_a = r#"
{
"type": "A",
"value": [1, 2, 3, 4, 5]
}"#;
let a: DataValue = serde_json::from_str(data_a)?;
如果将枚举变体命名为 A
和 B
,则可以省略 #[serde(rename = "…")]
属性。
这种序列化枚举的方式被称为"adjacent tagging"。您可以在 Serde documentation on enum serialization.
中了解标记枚举的各种选项您的 Data
结构包含多余的附加标签 data_type
。此信息已编码在枚举中,因此我认为您不需要此信息。如果你需要这个信息作为一个字符串,你可以在枚举中添加一个方法:
impl DataValue {
fn variant_name(&self) -> &'static str {
match self {
DataValue::TypeA(_) => "A",
DataValue::TypeB(_) => "B",
}
}
}