Кратко про Serde в Rust

от автора

Привет, Хабр!

Serde — это высокопроизводительная библиотека для сериализации и десериализации данных в Rust. Она поддерживает различные форматы данных, включая JSON, YAML, TOML, BSON и многие другие.

В этой статье рассмотрим основы Serde в Rust.

Установим

Для начала добавим Serde в проект. В файлике Cargo.toml добавляем следующие строки в раздел [dependencies]:

serde = { version = "1.0", features = ["derive"] } serde_json = "1.0"

Здесь подключаем не только саму библиотеку Serde, но и serde_json, которая даст вохможность работать с JSON-форматом. Функциональность Serde расширяется через различные адаптеры, так что в зависимости от нужд можно подключить и другие модули, такие как serde_yaml или serde_toml.

После добавления зависимостей запускаем команду cargo build для загрузки и компиляции Serde вместе с проектом.

Основы работы с Serde

Сериализация — это процесс преобразования структур данных Rust в формат, который можно легко передавать или хранить. Десериализация — это обратный процесс, преобразование данных из формата обратно в структуры данных Rust.

Для старта работы с Serde добавляем атрибут #[derive(Serialize, Deserialize)] к структурам данных. Это позволяет Serde автоматически генерировать код для сериализации и десериализации этих структур.

Пример сериализации и десериализации структуры в JSON:

use serde::{Serialize, Deserialize}; use serde_json::{to_string, from_str};  #[derive(Serialize, Deserialize)] struct User {     id: u32,     name: String,     email: String, }  fn main() {     // создание экземпляра структуры User     let user = User {         id: 1,         name: "Ivan Otus".to_string(),         email: "ivan.otus@example.com".to_string(),     };      // сериализация структуры User в JSON     let json = to_string(&user).unwrap();     println!("Serialized JSON: {}", json);      // десериализация JSON обратно в структуру User     let deserialized_user: User = from_str(&json).unwrap();     println!("Deserialized User: {:?}", deserialized_user); }

Serde имеет различные аннотации и атрибуты для настройки процесса сериализации и десериализации. Самые используемые:

  • #[serde(rename = "new_name")]: переименовывает поле при сериализации или десериализации.

  • #[serde(default)]: использует значение по умолчанию для поля, если оно отсутствует при десериализации.

  • #[serde(skip_serializing)]: пропускает поле при сериализации.

  • #[serde(skip_deserializing)]: пропускает поле при десериализации.

  • #[serde(with = "module")]: использует указанный модуль для сериализации и десериализации поля.

Попробуем использовать все сразу:

use serde::{Deserialize, Serialize}; use serde_with::serde_as;  #[serde_as] #[derive(Serialize, Deserialize)] struct User {     #[serde(rename = "userId")]     id: u32,     #[serde(default = "default_name")]     name: String,     #[serde(skip_serializing)]     password: String,     #[serde(skip_deserializing)]     secret: String,     #[serde(with = "serde_with::rust::display_fromstr")]     age: u32, }  fn default_name() -> String {     "Unknown".to_string() }  fn main() {     let user = User {         id: 1,         name: "Ivan Otus".to_string(),         password: "secret".to_string(),         secret: "hidden".to_string(),         age: 30,     };      let serialized = serde_json::to_string(&user).unwrap();     println!("Serialized: {}", serialized);      let deserialized: User = serde_json::from_str(&serialized).unwrap();     println!("Deserialized: {:?}", deserialized); }

#[serde(rename = "userId")] ренеймит поле id в userId при сериализации и десериализации.

#[serde(default = "default_name")] юзает функцию default_name для установки значения по умолчанию для поля name, если оно отсутствует при десериализации.

#[serde(skip_serializing)] скипает поле password при сериализации, так что оно не будет включено в JSON-строку.

#[serde(skip_deserializing)] пропускает поле secret при десериализации, так что его значение останется неизменным после десериализации.

#[serde(with = "serde_with::rust::display_fromstr")] использует модуль serde_with::rust::display_fromstr для сериализации и десериализации поля age. Годно для полей с пользовательскими типами, которые реализуют трейты Display и FromStr.

Кастомные сериализаторы и десериализаторы

В некоторых случаях может потребоваться более тонкая настройка процесса сериализации и десериализации, чем это позволяют стандартные механизмы Serde. В таких случаях можно использовать кастомные сериализаторы и десериализаторы.

Кастомный сериализатор — это функция или структура, реализующая трейт Serializer из Serde. Аналогично, кастомный десериализатор реализует трейт Deserializer.

Пример кастомного сериализатора для сериализации Option<String> в JSON как пустую строку, если значение None:

use serde::{Serialize, Serializer};  fn serialize_option_string<S>(value: &Option<String>, serializer: S) -> Result<S::Ok, S::Error> where     S: Serializer, {     match value {         Some(v) => serializer.serialize_str(v),         None => serializer.serialize_str(""),     } }  #[derive(Serialize)] struct MyStruct {     #[serde(serialize_with = "serialize_option_string")]     name: Option<String>, }  fn main() {     let my_struct = MyStruct {         name: None,     };      let json = serde_json::to_string(&my_struct).unwrap();     println!("Serialized JSON: {}", json); }

Допустим, есть структура Event, и нужно сериализовать её в JSON, где дата будет представлена в формате «гггг-мм-дд»:

use serde::{Serialize, Serializer}; use chrono::{DateTime, Utc, NaiveDate};  struct Event {     name: String,     date: DateTime<Utc>, }  fn serialize_date<S>(date: &DateTime<Utc>, serializer: S) -> Result<S::Ok, S::Error> where     S: Serializer, {     let formatted_date = date.format("%Y-%m-%d").to_string();     serializer.serialize_str(&formatted_date) }  impl Serialize for Event {     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>     where         S: Serializer,     {         let mut state = serializer.serialize_struct("Event", 2)?;         state.serialize_field("name", &self.name)?;         state.serialize_field("date", &serialize_date(&self.date, serializer)?)?;         state.end()     } }  fn main() {     let event = Event {         name: "RustConf".to_string(),         date: DateTime::from_utc(NaiveDate::from_ymd(2022, 9, 12).and_hms(0, 0, 0), Utc),     };      let serialized = serde_json::to_string(&event).unwrap();     println!("Serialized: {}", serialized); }

Аналогично, можно создать кастомный десериализатор, который будет преобразовывать строку в формате «гггг-мм-дд» обратно в DateTime<Utc>:

use serde::{Deserialize, Deserializer}; use chrono::{DateTime, Utc, NaiveDate}; use serde::de::{self, Visitor}; use std::fmt;  struct Event {     name: String,     date: DateTime<Utc>, }  struct DateTimeVisitor;  impl<'de> Visitor<'de> for DateTimeVisitor {     type Value = DateTime<Utc>;      fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {         formatter.write_str("a string in the format YYYY-MM-DD")     }      fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>     where         E: de::Error,     {         NaiveDate::parse_from_str(value, "%Y-%m-%d")             .map(|date| DateTime::from_utc(date.and_hms(0, 0, 0), Utc))             .map_err(de::Error::custom)     } }  fn deserialize_date<'de, D>(deserializer: D) -> Result<DateTime<Utc>, D::Error> where     D: Deserializer<'de>, {     deserializer.deserialize_str(DateTimeVisitor) }  #[derive(Deserialize)] struct Event {     name: String,     #[serde(deserialize_with = "deserialize_date")]     date: DateTime<Utc>, }  fn main() {     let data = r#"{"name": "RustConf", "date": "2022-09-12"}"#;     let event: Event = serde_json::from_str(data).unwrap();     println!("Deserialized: {:?}", event); }

Интеграция с другими либами

Serde может быть использована в Rocket для сериализации и десериализации данных, передаваемых в HTTP-запросах и ответах.

Пример использования Serde с Rocket для создания простого REST API:

#[macro_use] extern crate rocket;  use rocket::serde::{json::Json, Deserialize, Serialize};  #[derive(Serialize, Deserialize)] struct Task {     id: u32,     description: String, }  #[post("/tasks", format = "json", data = "<task>")] fn create_task(task: Json<Task>) -> Json<Task> {     task }  #[launch] fn rocket() -> _ {     rocket::build()         .mount("/", routes![create_task]) }

Определяем структуру Task, которая будет использоваться для сериализации и десериализации данных задач. создаем эндпойнт /tasks, который принимает JSON-данные и возвращает их обратно клиенту.

Tokio — это асинхронный рантайм для Rust, который позволяет писать высокопроизводительные асинхронные приложения. Пример использования Serde с Tokio для асинхронного чтения и записи JSON-данных:

use tokio::fs::File; use tokio::io::{self, AsyncReadExt, AsyncWriteExt}; use serde::{Deserialize, Serialize}; use serde_json;  #[derive(Serialize, Deserialize)] struct User {     id: u32,     name: String, }  async fn read_user_from_file(file_path: &str) -> io::Result<User> {     let mut file = File::open(file_path).await?;     let mut contents = String::new();     file.read_to_string(&mut contents).await?;     let user: User = serde_json::from_str(&contents)?;     Ok(user) }  async fn write_user_to_file(user: &User, file_path: &str) -> io::Result<()> {     let mut file = File::create(file_path).await?;     let contents = serde_json::to_string(user)?;     file.write_all(contents.as_bytes()).await?;     Ok(()) }  #[tokio::main] async fn main() -> io::Result<()> {     let user = User {         id: 1,         name: "Ivan Otus".to_string(),     };      write_user_to_file(&user, "user.json").await?;     let read_user = read_user_from_file("user.json").await?;     println!("Read user: {:?}", read_user);      Ok(()) }

Определяем структуру User и две асинхронные функции: read_user_from_file для чтения пользователя из JSON-файла и write_user_to_file для записи пользователя в JSON-файл. Юзаем Serde для сериализации и десериализации структуры User.

Подробней с Serde можно ознакомиться в документации.


Про востребованные языки программирования и практические инструменты мои коллеги из OTUS рассказывают в рамках онлайн-курсов. По ссылке вы можете ознакомиться с полным каталогом курсов, а также записаться на открытые уроки.


ссылка на оригинал статьи https://habr.com/ru/articles/806247/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *