使用 serde 序列化时如何按字母顺序对字段进行排序?

2024-04-08

我有一个 API,要求对象的字段按字母顺序排序,因为必须对结构进行哈希处理。

在 Java/Jackson 中,您可以在序列化器中设置一个标志:MapperFeature.SORT_PROPERTIES_ALPHABETICALLY。我在 Serde 中找不到类似的东西。

我在用着rmp-serde (消息包 https://msgpack.org/index.html)。它遵循 JSON 使用的注释和序列化过程,所以我认为它会完全兼容,但是@jonasbb 提供的排序 https://stackoverflow.com/questions/67789198/how-can-i-sort-fields-in-alphabetic-order-when-serializing-with-serde/67792465#67792465不适合它。

该结构具有(很多)嵌套的枚举和结构,必须将其展平才能得到最终表示。我在用着Serialize::serialize为此,但打电话state.serialize_field在正确的位置(这样一切都是按字母顺序排列的)是一种痛苦,因为枚举需要一个match子句,因此必须在不同位置对同一字段多次调用它,并且代码非常难以遵循。

作为可能的解决方案,有两个想法:

  1. 使用平面表示创建一个新结构,并手动按字母顺序对字段进行排序。

    这有点容易出错,因此针对这种扁平结构的编程排序解决方案会很棒。

  2. 缓冲键值Serialize::serialize(例如,在BTreeMap,已排序),并调用state.serialize_field在最后循环。

    问题是这些值似乎必须是类型Serialize,这不是对象安全的,所以我无法弄清楚如何将它们存储在地图中。

使用 serde 序列化时如何对 HashMap 键进行排序? https://stackoverflow.com/q/42723065/155423类似但不相关,因为我的问题是关于结构体字段/属性的排序。


您没有编写您的目标数据格式。这使得很难找到解决方案,因为有些解决方案可能并不适用于所有情况。

如果您使用 JSON,此代码将有效(除非preserve_order使用功能标志)。对于 TOML 来说也是如此,通过序列化为toml::Value作为中间步骤。 该解决方案也适用于其他数据格式,但可能会导致不同的序列化,例如,将数据作为映射而不是类似结构的形式发出。

fn sort_alphabetically<T: Serialize, S: serde::Serializer>(value: &T, serializer: S) -> Result<S::Ok, S::Error> {
    let value = serde_json::to_value(value).map_err(serde::ser::Error::custom)?;
    value.serialize(serializer)
}

#[derive(Serialize)]
struct SortAlphabetically<T: Serialize>(
    #[serde(serialize_with = "sort_alphabetically")]
    T
);

#[derive(Serialize, Deserialize, Default, Debug)]
struct Foo {
    z: (),
    bar: (),
    ZZZ: (),
    aAa: (),
    AaA: (),
}

println!("{}", serde_json::to_string_pretty(&SortAlphabetically(&Foo::default()))?);

因为结构必须被散列

虽然场序是非决定论的根源之一,但还有其他因素。许多格式允许不同数量的空格或不同的表示形式,例如 Unicode 转义\u0066.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用 serde 序列化时如何按字母顺序对字段进行排序? 的相关文章

随机推荐