How to conditionally deserialize JSON to two different variants of an enum?

痞子三分冷 提交于 2021-02-04 08:04:24

问题


Let's say I have JSON data like the following:

{
  "type": "A",
  "value": [ 1, 2, 3, 4, 5 ]
}
{
  "type": "B",
  "value": [ [ 1, 2, 3, 4, 5 ], [ 6, 7, 8 ] ]
}

type determines the type of value, which in the first example is Vec<u32> and in the second is Vec<Vec<u32>>.

If I represent the above data as follows:

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

struct Data {
  data_type: String,
  value: DataValue
}

How do I implement serde deserialization to properly decode these values?


回答1:


You can deserialize your JSON data directly to an instance of DataValue if you give Serde enough information to know how to do this:

#[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

If you name your enum variants A and B, you can omit the #[serde(rename = "…")] attributes.

This way of serializing enums is called "adjacent tagging". You can learn about the various options of tagging enums in the Serde documentation on enum serialization.

Your Data struct contains a redundant additional tag data_type. This information is already encoded in the enum, so I don't think you need this. If you need this information as a string, you can add a method to the enum:

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



回答2:


Fortunately serde has build-in support for 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);
}



回答3:


This may be a person opinion but I would generally try to avoid serialization / deserialization on enums.

Would this not be close to the same thing in C++?

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



来源:https://stackoverflow.com/questions/57639162/how-to-conditionally-deserialize-json-to-two-different-variants-of-an-enum

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!