How to deserialize JSON object of multiple key value entries to custom struct in Rust

Viewed 752

I am trying to deserialize a set of unknown key-value style labels from JSON into my struct.

This is my current implementation of parsing the JSON:

use std::collections::HashMap;
use serde::{Serialize, Deserialize};
use anyhow::Result;

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Node {
    metadata: Metadata,
    pub spec: Spec,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Metadata {
    name: String,
    labels: HashMap<String, String>,
    expires: String,
    id: i64,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Spec {
    pub hostname: String,
}

fn main() -> Result<()> {
    let json = r#"
[
  {
    "metadata": {
      "name": "161bee39-cf07-4e31-90ba-6593c9f505cb",
      "labels": {
        "application": "api",
        "owner": "team_x"
      },
      "expires": "2021-12-06T20:49:04.136656523Z",
      "id": 1638823144137190452
    },
    "spec": {
      "hostname": "host1.example.com"
    }
  },
  {
    "metadata": {
      "name": "c1b3ee09-8e4a-49d4-93b8-95cbcb676f20",
      "labels": {
        "application": "database",
        "owner": "team_y"
      },
      "expires": "2021-12-06T20:49:55.23841272Z",
      "id": 1638823195247684748
    },
    "spec": {
      "hostname": "host2.example.com"
    }
  }
]
    "#;
    let nodes: Vec<Node> = serde_json::from_str(json)?;
    println!("{:?}", nodes);
    Ok(())
}

The example works as it should, but now I would like to add a Label struct like this:

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Metadata {
    name: String,
    labels: Vec<Label>,
    expires: String,
    id: i64,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Label {
    key: String,
    value: String,
}

This does obviously not work, but I am unsure how to go on from here. From my research prior to this question, I know that you can implement a custom Deserializer, but I could not find out how to properly do that. Maybe this is also not the best approach and I am not seeing the obvious solution.

Thanks in advance for any example or help.

1 Answers

As of Stargateurs comment, the serde_with crate offers a solution to this:

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Metadata {
    name: String,
    #[serde(with = "serde_with::rust::tuple_list_as_map")]
    labels: Vec<Label>,
    expires: String,
    id: i64,
}

type Label = (String, String);
Related