rust 当使用serde序列化时如何排序HashMap键?

ojsjcaue  于 2023-01-02  发布在  其他
关注(0)|答案(2)|浏览(224)

我用serde序列化一个HashMap,如下所示:

#[derive(Serialize, Deserialize)]
struct MyStruct {
    map: HashMap<String, String>
}

HashMap的密钥顺序是未指定的,并且由于散列是随机化的(参见documentation),所以密钥实际上在相同的运行之间以不同的顺序出现。
我希望我的HashMap以排序(例如字母)键顺序序列化,以便序列化是确定的。
我可以使用BTreeMap而不是HashMap来实现这一点,因为BTreeMap::keys()按排序顺序返回其键,但是我不希望仅仅为了适应序列化逻辑而更改数据结构。
如何让serde在序列化之前对HashMap键进行排序?

kr98yfug

kr98yfug1#

使用serialize_with字段属性:

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());
}

在这里,我选择从HashMap重新构建整个BTreeMap,然后重用现有的序列化实现。

{
  "map": {
    "alpha": "1",
    "beta": "2",
    "gamma": "3"
  }
}
mf98qq94

mf98qq942#

这是一种稍微通用一点的自动排序方法,它使用itertools,并且只依赖于std库。

// This requires itertools crate
pub fn sorted_map<S: Serializer, K: Serialize + Ord, V: Serialize>(
    value: &HashMap<K, V>,
    serializer: S,
) -> Result<S::Ok, S::Error> {
    value
        .iter()
        .sorted_by_key(|v| v.0)
        .collect::<BTreeMap<_, _>>()
        .serialize(serializer)
}

// This only uses std
pub fn sorted_map<S: Serializer, K: Serialize + Ord, V: Serialize>(
    value: &HashMap<K, V>,
    serializer: S,
) -> Result<S::Ok, S::Error> {
    let mut items: Vec<(_, _)> = value.iter().collect();
    items.sort_by(|a, b| a.0.cmp(&b.0));
    BTreeMap::from_iter(items).serialize(serializer)
}

上述两个函数都可以与以下结构一起使用:

#[derive(Serialize)]
pub struct Obj1 {
    #[serde(serialize_with = "sorted_map")]
    pub table: HashMap<&'static str, i32>,
}

#[derive(Serialize)]
pub struct Obj2 {
    #[serde(serialize_with = "sorted_map")]
    pub table: HashMap<String, i32>,
}

相关问题