Serde_with KeyValueMap usage

I'm trying to replicate the behavior described on serde_with's user guide of using one of a struct fields' as a key with the toml crate but to no avail.

The following source code:

use std::error;

use serde::{Deserialize, Serialize};
use serde_with::{serde_as, KeyValueMap};

#[derive(Debug, Deserialize, Serialize)]
struct Entry {
    #[serde(rename = "$key$")]
    should_be_key: String,
    field: String,
}

#[serde_as]
#[derive(Debug, Deserialize, Serialize)]
struct Entries {
    #[serde_as(as = "KeyValueMap<_>")]
    entries: Vec<Entry>,
}

fn main() -> Result<(), Box<dyn error::Error>> {
    let contents = toml::toml! {
        [[entries]]
        should_be_key = "keyname1"
        field = "field value of keyname1"

        [[entries]]
        should_be_key = "keyname2"
        field = "field value of keyname2"
    }
    .to_string();

    println!("{:#?}", contents);

    let entries: Entries = match toml::from_str(&contents) {
        Ok(r) => r,
        Err(e) => {
            eprintln!("{}", e);
            return Err("Failed to deserialize".into());
        }
    };

    println!("{:#?}", entries);

    Ok(())
}

Returns this error message:

[user@hostname:~/project-root]$ cargo run
TOML parse error at line 1, column 1
  |
1 | [[entries]]
  | ^^^^^^^^^^^
invalid type: sequence, expected a map

Error: "Failed to deserialize"

What could be causing this error? Perhaps the usage of TOML's array of tables?

No, this has nothing to do with TOML. You have not one but two errors in your own code:

  1. You are not using a newtype struct for the map, and
  2. you are simply giving the wrong structure to the deserializer. It's completely different from what they have in the example; why would you expect it to work? The deserializer should be fed a map of structs, but you are giving it an array of maps.

This works:

use std::error;

use serde::{Deserialize, Serialize};
use serde_with::{serde_as, KeyValueMap};

#[derive(Debug, Deserialize, Serialize)]
struct Entry {
    #[serde(rename = "$key$")]
    should_be_key: String,
    field: String,
}

#[serde_as]
#[derive(Debug, Deserialize, Serialize)]
struct Entries(
    #[serde_as(as = "KeyValueMap<_>")]
    Vec<Entry>,
);

fn main() -> Result<(), Box<dyn error::Error>> {
    let contents = toml::toml! {
        "keyname1" = {field = "field value of keyname1"}
        "keyname2" = {field = "field value of keyname2"}
    }
    .to_string();

    println!("{contents}");

    let entries: Entries = toml::from_str(&contents)?;

    println!("{entries:#?}");

    Ok(())
}
1 Like

Thanks for the explanation!, @H2CO3. I can now understand what I was
doing wrong.