Как я могу отсортировать поля в алфавитном порядке при сериализации с помощью serde?

У меня есть API, который требует, чтобы поля объекта были отсортированы в алфавитном порядке, потому что структура должна быть хеширована.

В Java / Jackson вы можете установить флаг в сериализаторе: MapperFeature.SORT_PROPERTIES_ALPHABETICALLY. Ничего подобного в Серде не найду.

Я использую rmp-serde (MessagePack). Он следует за аннотациями и процессом сериализации, используемым для JSON, поэтому я подумал, что он будет полностью совместим, но сортировка, предоставляемая @jonasbb, для этого не работает.

Структура имеет (много) вложенных перечислений и структур, которые необходимо выровнять для окончательного представления. Я использую для этого Serialize::serialize, но вызов state.serialize_field в нужном месте (чтобы все было в алфавитном порядке) - это боль, потому что перечислениям требуется предложение match, поэтому его нужно вызывать несколько раз для одного и того же поля в разных местах и код очень труден для понимания.

В качестве возможных решений две идеи:

  1. Создайте новую структуру с плоским представлением и отсортируйте поля в алфавитном порядке вручную.

    Это немного подвержено ошибкам, поэтому решение программной сортировки для этой уплощенной структуры было бы отличным.

  2. Сохраните ключевые значения в Serialize::serialize (например, в BTreeMap, который отсортирован) и вызовите state.serialize_field в цикле в конце.

    Проблема в том, что значения, похоже, должны быть типа Serialize, что небезопасно для объектов, поэтому я не смог понять, как сохранить их на карте.

Как отсортировать ключи HashMap при сериализации с помощью serde? аналогичен, но не связан, потому что мой вопрос касается сортировки полей структуры /характеристики.


person user    schedule 01.06.2021    source источник
comment
Если это массивы, просто предварительно отсортируйте их. Если они похожи на объекты, это не имеет особого значения, потому что объект десериализации может произвольно отменить их порядок в зависимости от того, что это такое.   -  person Netwave    schedule 01.06.2021
comment
@Netwave объектно-подобный, и это имеет значение, потому что конечная точка, с которой я взаимодействую, требует, чтобы они были отсортированы по алфавиту.   -  person user    schedule 01.06.2021
comment
@Netwave Наиболее распространенная структура C # json также требует, чтобы $ type был первым, но это проблема только в том случае, если вы не можете изменить получатель (или получателю требуется высокая производительность) и если вам нужно иметь динамические типы в структуре данных . Я хочу сказать, что приказ имеет значение.   -  person piojo    schedule 01.06.2021


Ответы (1)


Вы не пишете, какой формат данных вы нацеливаете. Это затрудняет поиск решения, поскольку некоторые из них могут работать не во всех случаях.

Этот код работает, если вы используете 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.

person jonasbb    schedule 01.06.2021
comment
Это волшебство! не вижу ничего с сортировкой, кроме выбранных самостоятельно имен ..: D Я не предоставил некоторую информацию, извините. Во-первых, я использую rmp_serde. Я попытался использовать его в sort_alphabeticallyto_vec_named, что на самом деле не имеет смысла), он скомпилирован, но вывод не отсортирован. - person user; 01.06.2021
comment
Второй (который, вероятно, должен получить новый пост) заключается в том, что сериализуемая структура немного сложна, у нее есть перечисления с вложенными структурами, и все должно быть сглажено в JSON. Эта ручная сериализация уже работает (impl Serialize). Но правильно упорядочивать поля вручную очень обременительно из-за наличия вложенных перечислений и уплощения. Я думал об использовании BTreeMap в качестве буфера, но не смог установить тип значения Serialize, потому что это небезопасно для объектов. Не уверен, что это вообще хорошая идея. - person user; 01.06.2021
comment
Что касается хеширования, это требование стороннего сервера, с которым я работаю. Чтобы вы посоветовали? - person user; 01.06.2021
comment
serde_json::Value::Map внутри использует BTreeMap, поэтому ключи сортируются. То же для toml. Я не знаком с rmp, поэтому не знаю, возникают ли упомянутые проблемы и там. Расплывчатого упоминания о хешировании недостаточно, чтобы дать подсказку. - person jonasbb; 01.06.2021