如何有条件地将 JSON 反序列化为枚举的两个不同变体?

Lis*_*one 5 json rust serde

假设我有如下 JSON 数据:

{
  "type": "A",
  "value": [ 1, 2, 3, 4, 5 ]
}
Run Code Online (Sandbox Code Playgroud)
{
  "type": "B",
  "value": [ [ 1, 2, 3, 4, 5 ], [ 6, 7, 8 ] ]
}
Run Code Online (Sandbox Code Playgroud)

type确定 的类型value,在第一个示例中为Vec<u32>,在第二个示例中为Vec<Vec<u32>>

如果我将上面的数据表示如下:

{
  "type": "A",
  "value": [ 1, 2, 3, 4, 5 ]
}
Run Code Online (Sandbox Code Playgroud)

如何实现 serde 反序列化以正确解码这些值?

Sve*_*ach 10

DataValue如果您为 Serde 提供足够的信息以了解如何执行此操作,则可以将 JSON 数据直接反序列化为 的实例:

\n\n
#[derive(Debug, Deserialize)]\n#[serde(tag = "type", content = "value")]\nenum DataValue {\n    #[serde(rename = "A")]\n    TypeA(Vec<u32>),\n    #[serde(rename = "B")]\n    TypeB(Vec<Vec<u32>>),\n}\n\nlet data_a = r#"\n    {\n        "type": "A",\n        "value": [1, 2, 3, 4, 5]\n    }"#;\nlet a: DataValue = serde_json::from_str(data_a)?;\n
Run Code Online (Sandbox Code Playgroud)\n\n

操场

\n\n

如果将枚举变体命名为AB,则可以省略#[serde(rename = "\xe2\x80\xa6")]属性。

\n\n

这种序列化枚举的方式称为“相邻标记”。您可以在有关枚举序列化的 Serde 文档中了解标记枚举的各种选项。

\n\n

您的Data结构包含冗余的附加标签data_type。此信息已编码在枚举中,因此我认为您不需要此信息。如果您需要此信息作为字符串,您可以向枚举添加一个方法:

\n\n
impl DataValue {\n    fn variant_name(&self) -> &\'static str {\n        match self {\n            DataValue::TypeA(_) => "A",\n            DataValue::TypeB(_) => "B",\n        }\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n


edw*_*rdw 5

幸运的是,serde内置了对枚举类型的支持:

//# 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);
}
Run Code Online (Sandbox Code Playgroud)