【问题标题】:How can I sort fields in alphabetic order when serializing with serde?使用 serde 序列化时如何按字母顺序对字段进行排序?
【发布时间】:2021-06-01 12:50:08
【问题描述】:

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

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

我正在使用rmp-serde (MessagePack)。它遵循用于JSON的注释和序列化过程,所以我认为它会完全兼容,但是the sorting provided by @jonasbb不适合它。

该结构有(很多)嵌套的枚举和结构,它们必须被展平以获得最终表示。我为此使用Serialize::serialize,但在正确的位置调用state.serialize_field(这样所有内容都按字母顺序排列)很痛苦,因为枚举需要match 子句,因此必须多次调用它字段在不同的地方,代码很难理解。

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

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

    这有点容易出错,所以这个扁平结构的编程排序解决方案会很棒。

  2. 缓存Serialize::serialize中的键值(例如在一个BTreeMap中,已排序),最后在循环中调用state.serialize_field

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

How to sort HashMap keys when serializing with serde? 相似但不相关,因为我的问题是关于结构的字段/属性的排序。

【问题讨论】:

  • 如果它们是数组,只需对它们进行预排序。如果它们是类似对象的,那并不重要,因为反序列化对象可以根据它的内容任意对它们进行排序。
  • @Netwave 它类似于对象,这很重要,因为我与之交互的端点要求它们按字母顺序排序。
  • @Netwave 最常见的 C# json 框架也需要 "$type" 是第一个,但这只是一个问题,如果你不能修改接收器(或接收器需要高性能),如果您需要在数据结构中具有动态类型。我的观点是,订单的重要性并非闻所未闻。

标签: rust serde


【解决方案1】:

您没有编写您所针对的数据格式。这使得很难找到解决方案,因为有些解决方案可能并非在所有情况下都有效。

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

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

【讨论】:

  • 这太神奇了!没有看到任何与自行选择的名称排序的东西.. :D 我没有提供一些信息,抱歉。第一个是我使用rmp_serde。我尝试在sort_alphabetically 中使用它(使用to_vec_named,这并没有什么意义),它编译但输出没有排序。
  • 第二个(可能应该得到一个新帖子)是要序列化的结构有点复杂,它具有嵌套结构的枚举,并且所有内容都需要在 JSON 中展平。此手动序列化已经开始工作 (impl Serialize)。但是手动正确排序字段非常麻烦,因为所有嵌套枚举和展平。我曾考虑使用BTreeMap 作为缓冲区,但无法将值类型设置为Serialize,因为它不是对象安全的。不确定这是否是一个好主意。
  • 关于散列,这是我正在使用的“第三方”后端的要求。你会推荐什么?
  • serde_json::Value::Map 内部使用BTreeMap,因此键已排序。 toml 也是如此。我对 rmp 不熟悉,所以我不知道上面提到的问题是否也发生在那里。对哈希的模糊提及并不足以提供提示。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多