How do I use Serde to serialize a HashMap with structs as keys to JSON?

I want to serialize a HashMap with structs as keys:

extern crate serde_json; // 1.0.22
#[macro_use]
extern crate serde_derive; // 1.0.68

use std::collections::HashMap;

fn main() {
    #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash)]
    struct Foo {
        x: u64,
    }

    #[derive(Serialize, Deserialize, Debug)]
    struct Bar {
        x: HashMap<Foo, f64>,
    }

    let mut p = Bar { x: HashMap::new() };
    p.x.insert(Foo { x: 0 }, 0.0);
    let serialized = serde_json::to_string(&p).unwrap();
}

This code compiles, but when I run it I get an error:

Error("key must be a string", line: 0, column: 0)'

I changed the code:

#[derive(Serialize, Deserialize, Debug)]
struct Bar {
    x: HashMap<u64, f64>,
}

let mut p = Bar { x: HashMap::new() };
p.x.insert(0, 0.0);
let serialized = serde_json::to_string(&p).unwrap();

The key in the HashMap is now a u64 instead of a string. Why does the first code give an error?

1 answer

  • answered 2018-07-11 11:15 dotPoozer

    In order to serialize your custom data type into JSON (or any other format), you need to implement the Serialize trait. Otherwise Serde doesn't know how to serialize your type.

    It should go something along these lines:

    impl Display for Foo {
        fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
            write!(f, "{}", self.x)
        }
    }
    
    impl Serialize for Bar {
        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
        where
            S: Serializer,
        {
            let mut map = serializer.serialize_map(Some(self.x.len()))?;
            for (k, v) in &self.x {
                map.serialize_entry(&k.to_string(), &v)?;
            }
            map.end()
        }
    }
    

    (playground)

    If your data structure is an enum, then you will need to change Foo's Display implementation.