В Rust сериализация и десериализация данных являются достаточно частыми задачами, особенно при интеграции с веб-сервисами, базами данных или обмене сообщениями между компонентами. Наиболее популярная библиотека для этого — serde, которая стала фактическим стандартом де-факто для работы с сериализацией в Rust.
История вопроса
Разработчики Rust столкнулись с необходимостью гибкой и производительной сериализации. Изначально существовала масса специализированных решений, но именно serde предложил удобную интеграцию с derive-макросами, гибкую схему поддержки различных форматов (JSON, CBOR, BSON, TOML, YAML и другие).
Проблема
Необходимо гарантировать корректную трансформацию Rust-структур в форматы обмена данными (например JSON), а также обратно, не потеряв типовую безопасность и не допустив "тихих ошибок" (silent fail). Проблемы часто вызваны несоответствием структуры данных или попыткой сериализовать неподдерживаемые типы.
Решение
Для сериализации структур нужно реализовать трейты Serialize и Deserialize, обычно с помощью #[derive(Serialize, Deserialize)]. Для нестандартных случаев можно реализовать эти трейты вручную или воспользоваться атрибутами, управляющими схемой сериализации.
Пример кода:
use serde::{Serialize, Deserialize}; #[derive(Serialize, Deserialize, Debug)] struct Person { name: String, age: u8, } fn main() { let data = Person { name: "Bob".into(), age: 32 }; let json = serde_json::to_string(&data).unwrap(); println!("{}", json); let obj: Person = serde_json::from_str(&json).unwrap(); println!("{:?}", obj); }
Ключевые особенности:
Как сериализовать/десериализовать Option-поля или поля с дефолтными значениями?
Можно использовать #[serde(default)] для указания значения по умолчанию или #[serde(skip_serializing_if = "Option::is_none")], чтобы не сериализовать None.
Пример кода:
#[derive(Serialize, Deserialize)] struct Config { #[serde(default)] timeout: Option<u32>, }
Можно ли сериализовать структуры с custom-полями или вычисляемыми значениями (например fn get_hash())?
Да, но такие поля нужно помечать #[serde(skip)], если они не должны быть сериализованы, либо реализовать Serialize/Deserialize вручную, если нужно сериализовать вычисленные значения.
Что произойдет, если структура в Rust не совпадает по полям с JSON-объектом (отсутствует поле, или появляется новое)?
Po умолчанию serde игнорирует лишние поля в JSON (если не включен строгий режим), и выдаст ошибку при отсутствии обязательных полей структуры (если не задан #[serde(default)]).
Приходит внешний JSON с лишними и отсутствующими полями, а структура не использует #[serde(default)],
Плюсы:
Минусы:
Используется #[serde(default)] и #[serde(skip_serializing_if)], все поля валидируются, лишние игнорируются.
Плюсы:
Минусы: