如何有条件地将 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)?;

Playground

如果将枚举变体命名为 AB,则可以省略 #[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",
        }
    }
}