0

I am trying to deserialize JSON data that looks like the following using serde:

{
  "item1": "Foo",
  "item2": "Bar",
  "item3": "Baz",
  "item4": null,
  "item5": null,
  "description1": "something",
  "description2": "another thing",
  "description3": "one more thing",
  "description4": null,
  "description5": null
}

It can be assumed that there are only 5 items and the fields will always be present (but possibly null).

I was hoping to place it into a Vec<Item> where

struct Item {
    name: String,
    description: String,
}

I am aware that I can #[derive(Deserialize)] and alias the name field, but I'm not sure how to handle transforming the numbered items into a list. How would one go about handling a case like this in serde?

carloabelli
  • 4,289
  • 3
  • 43
  • 70
  • If you control the JSON format, you should change it to something sane. If you don't control the format, the easiest solution is to define an intermediary type with the same layout as the JSON, and implement `From` for your target type. – Sven Marnach Mar 07 '20 at 00:32
  • 1
    If you want to, you could use `#[serde(from = Intermediary)]` on the target type, so serde will do the conversion for you. – Sven Marnach Mar 07 '20 at 00:33
  • @SvenMarnach Unfortunately I don’t control it. That makes sense. – carloabelli Mar 07 '20 at 02:08

1 Answers1

1

Consider serde_json::from_str to parse into a serde_json::Value and then apply the transformation needed for your specific use case.

#[derive(Debug)]
struct Item {
    name: String,
    description: String,
}

fn main() {
    let data = r#"
    {
        "item1": "Foo",
        "item2": "Bar",
        "item4": null,
        "item5": null,
        "description1": "something",
        "description2": "another thing",
        "description3": "one more thing",
        "description4": null,
        "description5": null
    }"#;
    let v: Value = serde_json::from_str(data).unwrap();
    let mut items = Vec::new();

    for idx in 1..6 {
        let name = format!("item{}", idx);
        let descr = format!("description{}", idx);
        if let (Value::String(value), Value::String(descr)) = (&v[name], &v[descr]) {
            items.push(Item {
                name: value.to_string(),
                description: descr.to_string(),
            })
        }
    }

    println!("items {:?}", items);
}
attdona
  • 17,196
  • 7
  • 49
  • 60