Typhaon
Typhaon

Reputation: 1054

How do I deserialize an array of objects from TOML to Rust?

I'm trying to deserialize an array of objects. The current code that's doing that is:

let jobs: Vec<Job> = toml::from_str(include_str!("../../assets/job.toml"))
    .expect("Default job not loading");

But it's returning this error:

thread 'main' panicked at 'Default job not loading: Error { inner: ErrorInner { kind: Custom, line: Some(6), col: 0, at: Some(556), message: "invalid type: map, expected a sequence", key: [] } }'

I have this TOML file:

[[job]]
title = "Lorem"
company = "Ipsum"
description = "Praesent fermentum, nulla eu vehicula dapibus, sapien ipsum tincidunt felis, a pharetra nibh purus vel lacus. Suspendisse lectus leo, vestibulum eget ligula ut, facilisis porttitor odio. Morbi egestas lacinia nisi, sed ornare arcu iaculis sed. Ut tempor condimentum mattis. Quisque auctor scelerisque purus. Donec tincidunt sagittis sapien. Ut ac malesuada est, in condimentum ex. Curabitur in sapien non elit sagittis imperdiet at eget lectus."
[[job]]
title = "Lorem"
company = "Ipsum"
description = "Praesent fermentum, nulla eu vehicula dapibus, sapien ipsum tincidunt felis, a pharetra nibh purus vel lacus. Suspendisse lectus leo, vestibulum eget ligula ut, facilisis porttitor odio. Morbi egestas lacinia nisi, sed ornare arcu iaculis sed. Ut tempor condimentum mattis. Quisque auctor scelerisque purus. Donec tincidunt sagittis sapien. Ut ac malesuada est, in condimentum ex. Curabitur in sapien non elit sagittis imperdiet at eget lectus."

For this struct:

#[derive(Debug, Deserialize)]
pub struct Job {
    pub title: String,
    pub company: String,
    pub description: String,
    // shortened for example
}

Everywhere I look I expect [[job]] to be a vector. But why is it bein treated like a map?

The issue isn't the capitalization.

Upvotes: 2

Views: 1095

Answers (1)

at54321
at54321

Reputation: 11708

This is what your TOML would look like if it were JSON:

{
  "job": [
    {
      "title": "Lorem",
      "company": "Ipsum",
      "description": "Praesent fermentum, nulla eu vehicula dapibus, sapien ipsum tincidunt felis, a pharetra nibh purus vel lacus. Suspendisse lectus leo, vestibulum eget ligula ut, facilisis porttitor odio. Morbi egestas lacinia nisi, sed ornare arcu iaculis sed. Ut tempor condimentum mattis. Quisque auctor scelerisque purus. Donec tincidunt sagittis sapien. Ut ac malesuada est, in condimentum ex. Curabitur in sapien non elit sagittis imperdiet at eget lectus."
    },
    {
      "title": "Lorem",
      "company": "Ipsum",
      "description": "Praesent fermentum, nulla eu vehicula dapibus, sapien ipsum tincidunt felis, a pharetra nibh purus vel lacus. Suspendisse lectus leo, vestibulum eget ligula ut, facilisis porttitor odio. Morbi egestas lacinia nisi, sed ornare arcu iaculis sed. Ut tempor condimentum mattis. Quisque auctor scelerisque purus. Donec tincidunt sagittis sapien. Ut ac malesuada est, in condimentum ex. Curabitur in sapien non elit sagittis imperdiet at eget lectus."
    }
  ]
}

Now it's a bit more obvious why this should be deserialized into an object with a single key "job".

You can try to run this code:

use toml::Value;
pub fn main() {
    let s = "
        [[job]]
        title = \"Lorem\"
        company = \"Ipsum\"
        description = \"abcd\"
        [[job]]
        title = \"Lorem\"
        company = \"Ipsum\"
        description = \"abcd\"
        ";
    let v = s.parse::<Value>().unwrap();
    println!("{v:?}");
}

What gets printed is:

Table({"job": Array([Table({"company": String("Ipsum"), "description": String("abcd"), "title": String("Lorem")}), Table({"company": String("Ipsum"), "description": String("abcd"), "title": String("Lorem")})])})

In the toml crate a Table is defined as:

type Table = Map<String, Value>;

Also from the docs:

By default it is backed by a BTreeMap, enable the preserve_order feature to use a LinkedHashMap instead.

So Table is pretty much like a "map" or "object".

Upvotes: 2

Related Questions