I'm serializing a HashMap
with serde, like so:
#[derive(Serialize, Deserialize)]
struct MyStruct {
map: HashMap<String, String>
}
HashMap
's key order is unspecified, and since the hashing is randomized (see documentation), the keys actually end up coming out in different order between identical runs.
I'd like my HashMap
to be serialized in sorted (e.g. alphabetical) key order, so that the serialization is deterministic.
I could use a BTreeMap
instead of a HashMap
to achieve this, as BTreeMap::keys()
returns its keys in sorted order, but I'd rather not change my data structure just to accommodate the serialization logic.
How do I tell serde to sort the HashMap
keys before serializing?
Use the serialize_with
field attribute:
use serde::{Deserialize, Serialize, Serializer}; // 1.0.106
use serde_json; // 1.0.52
use std::collections::{BTreeMap, HashMap};
#[derive(Serialize, Deserialize, Default)]
struct MyStruct {
#[serde(serialize_with = "ordered_map")]
map: HashMap<String, String>,
}
fn ordered_map<S>(value: &HashMap<String, String>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let ordered: BTreeMap<_, _> = value.iter().collect();
ordered.serialize(serializer)
}
fn main() {
let mut m = MyStruct::default();
m.map.insert("gamma".into(), "3".into());
m.map.insert("alpha".into(), "1".into());
m.map.insert("beta".into(), "2".into());
println!("{}", serde_json::to_string_pretty(&m).unwrap());
}
Here, I've chosen to just rebuild an entire BTreeMap
from the HashMap
and then reuse the existing serialization implementation.
{
"map": {
"alpha": "1",
"beta": "2",
"gamma": "3"
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With