Deserialize variable meta object with serde

十年热恋 提交于 2021-01-28 12:21:44

问题


I am looking an elegant way to deserialize the following input:

{
  "products": [

    {
      "id": 1,
      "ptype": "Clothes",
      "description": "some data about clothes",
      "metadata": {
        "colors" : ["blue", "green"],
        "web": false,
        "size": 2
      }
    },
    {
      "id": 4,
      "ptype": "Food",
      "description": "text for foods",
      "metadata": {
        "country": "France",
        "wine": true
      }
    },
    {
      "id": 12,
      "ptype": "EmptyPlaceholder",
      "description": "nothing at all",
      "metadata": {
      }
    }
  ]
}

The json contains an array of products. A product can be identified by a ptype field. According to the type of the field the metadata object differs. For example if the ptype is Food, then the metadata for food will be a string (country) and a boolean value (wine). So the products have some fields in common, id, ptype and description and some metadata. I want to deserialize this JSON file in a Vec<Product>.

So far I have used the following code:

use serde::{Deserialize};
use serde_json::Result;

#[derive(Deserialize, Debug)]
struct ClothesData {
    colors : Vec<String>,
    web : bool,
    size: u32,
}

#[derive(Deserialize, Debug)]
struct FoodData {
    country: String,
    wine: bool,
}

#[derive(Deserialize, Debug)]
struct EmptyData {

}

#[derive(Deserialize, Debug)]
enum Metadata {
    ClothesData,
    FoodData,
    EmptyData,
}

#[derive(Deserialize, Debug)]
enum  Ptype {
    Clothes,
    Food,
    EmptyPlaceholder
}

#[derive(Deserialize, Debug)]
struct Product {
    id: u32,
    ptype: Ptype,
    description: Option<String>,
    metadata: Metadata,

}

I am not sure how to proceed from here and I would like to ask if serde crate can do this "automatically".


回答1:


This corresponds to the "adjacently tagged" serde enum representation:

use serde::Deserialize;
use serde_json::Result;

#[derive(Deserialize, Debug)]
struct Clothes {
    colors: Vec<String>,
    web: bool,
    size: u32,
}

#[derive(Deserialize, Debug)]
struct Food {
    country: String,
    wine: bool,
}

#[derive(Deserialize, Debug)]
struct Empty {}

#[derive(Deserialize, Debug)]
#[serde(tag = "ptype", content = "metadata")]
enum Kind {
    Clothes(Clothes),
    Food(Food),
    EmptyPlaceholder(Empty),
}

#[derive(Deserialize, Debug)]
struct Product {
    id: u32,
    description: Option<String>,
    #[serde(flatten)]
    kind: Kind,
}

#[derive(Deserialize, Debug)]
struct Root {
    products: Vec<Product>,
}

fn main() -> Result<()> {
    let data = r#"
    {
        "products": [

            {
              "id": 1,
              "ptype": "Clothes",
              "description": "some data about clothes",
              "metadata": {
                "colors" : ["blue", "green"],
                "web": false,
                "size": 2
              }
            },
            {
              "id": 4,
              "ptype": "Food",
              "description": "text for foods",
              "metadata": {
                "country": "France",
                "wine": true
              }
            },
            {
              "id": 12,
              "ptype": "EmptyPlaceholder",
              "description": "nothing at all",
              "metadata": {
              }
            }
        ]
    }"#;

    let root: Root = serde_json::from_str(data)?;
    println!("{:#?}", root);

    Ok(())
}


来源:https://stackoverflow.com/questions/61645924/deserialize-variable-meta-object-with-serde

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