Home > other >  Parsing a nested JSON object
Parsing a nested JSON object

Time:05-19

I'm trying to parse a JSON file with the following loose format using serde_json in Rust:

{
  "Source_n": {
    "Destination_n": {
      "distance": 2,
      "connections": [
        {
          "color": "Any",
          "locomotives": 0,
          "tunnels": 0
        }
      ]
    }
...

where Source and Destination can be any number of keys (Link to full file).

I've created the following structs in an attempt to deseralize the JSON:

#[derive(Debug, Deserialize)]
struct L0 {
    routes: HashMap<String, L1>,
}

#[derive(Debug, Deserialize)]
struct L1 {
    destination_city: HashMap<String, L2>,
}

#[derive(Debug, Deserialize)]
struct L2 {
    distance: u8,
    connections: Vec<L3>,
}

#[derive(Debug, Deserialize, Clone)]
struct L3 {
    color: String,
    locomotives: u8,
    tunnels: u8,
}

When I try to read the JSON as an L0 object I get a panic on this line:

let data: L0 = serde_json::from_str(&route_file_as_string).unwrap();

Panic:

    Finished dev [unoptimized   debuginfo] target(s) in 0.01s
     Running `target/debug/ticket-to-ride`
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Error("missing field `routes`", line: 1889, column: 1)', src/route.rs:39:64
stack backtrace:
   0: rust_begin_unwind
             at /rustc/59eed8a2aac0230a8b53e89d4e99d55912ba6b35/library/std/src/panicking.rs:517:5
   1: core::panicking::panic_fmt
             at /rustc/59eed8a2aac0230a8b53e89d4e99d55912ba6b35/library/core/src/panicking.rs:101:14
   2: core::result::unwrap_failed
             at /rustc/59eed8a2aac0230a8b53e89d4e99d55912ba6b35/library/core/src/result.rs:1617:5
   3: core::result::Result<T,E>::unwrap
             at /rustc/59eed8a2aac0230a8b53e89d4e99d55912ba6b35/library/core/src/result.rs:1299:23
   4: ticket_to_ride::route::route_file_to_L0
             at ./src/route.rs:39:20
   5: ticket_to_ride::route::routes_from_file
             at ./src/route.rs:44:33
   6: ticket_to_ride::main
             at ./src/main.rs:6:5
   7: core::ops::function::FnOnce::call_once
             at /rustc/59eed8a2aac0230a8b53e89d4e99d55912ba6b35/library/core/src/ops/function.rs:227:5

I've been able to read the JSON as a HashMap<String, Value> object, but whenever I try to start working at the lower levels I get an error. It seems to be looking for a key named routes, but what I actually want to just a nested HashMap, similar to how you can read a JSON in Python in a nested fashion.

Any advice on how to proceed? Is what I'm attempting reasonable with this library?

CodePudding user response:

As Sven Marnach says in their comment, add #[serde(flatten)] to create the HashMap from data that uses keys as JSON fields:

#[derive(Debug, Deserialize)]
struct L0 {
    #[serde(flatten)]
    routes: HashMap<String, L1>,
}

#[derive(Debug, Deserialize)]
struct L1 {
    #[serde(flatten)]
    destination_city: HashMap<String, L2>,
}
  • Related