6

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
  • well... like that xd Commented Aug 25, 2019 at 13:48

3 Answers 3

10

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",
        }
    }
}
Sign up to request clarification or add additional context in comments.

Comments

5

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);
}

Comments

-7

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
}

2 Comments

That's... not even legal C++? It's not clear to me what argument you're making besides "don't do that", which is not an answer to the question.
This neither fits the expression of the problem nor semantically make sense. Even after pretending it is valid C++, it would say that you have both TypeA and TypeB data valid. This represents a huge class of bugs in software. Please unlearn this and do not perpetuate this.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.