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

时间:2019-08-24 15:07:46

标签: json rust serde

比方说我有如下的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反序列化以正确解码这些值?

3 个答案:

答案 0 :(得分:1)

幸运的是,serdeenum 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);
}

答案 1 :(得分:1)

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

#[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 = "…")]属性。

这种枚举序列的方式称为“相邻标记”。您可以在Serde documentation on enum serialization中了解标记枚举的各种选项。

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

impl DataValue {
    fn variant_name(&self) -> &'static str {
        match self {
            DataValue::TypeA(_) => "A",
            DataValue::TypeB(_) => "B",
        }
    }
}

答案 2 :(得分:-3)

这可能是一个人的看法,但我通常会尽量避免对枚举进行序列化/反序列化。

在C ++中会不会接近同一件事?

struct DataValue final{
  TypeA(Vec<u32>) final,
  TypeB(Vec<Vec<u32>>) final
}