JSON обработка в Rust: полное руководство с serde_json
JSON (JavaScript Object Notation) — один из самых популярных форматов обмена данными в веб-разработке. В Rust обработка JSON выполняется с помощью мощной библиотеки serde_json, которая является частью экосистемы Serde — фреймворка для сериализации и десериализации структур данных. В этой статье мы подробно разберём, как работать с JSON в Rust: от установки до сложных примеров с вложенными структурами.
Установка и настройка
Для начала работы добавьте зависимости в файл Cargo.toml:
[dependencies]
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
Библиотека serde предоставляет макросы #[derive(Serialize, Deserialize)], а serde_json отвечает за преобразование данных в JSON и обратно.
Базовая сериализация и десериализация
Рассмотрим простой пример: создадим структуру User и научимся преобразовывать её в JSON и обратно.
use serde::{Serialize, Deserialize};
use serde_json;
#[derive(Debug, Serialize, Deserialize)]
struct User {
name: String,
age: u8,
email: String,
}
fn main() {
// Создаём экземпляр структуры
let user = User {
name: "Алексей".to_string(),
age: 30,
email: "alex@example.com".to_string(),
};
// Сериализация в строку JSON
let json_string = serde_json::to_string(&user).unwrap();
println!("JSON: {}", json_string);
// Десериализация из JSON обратно в структуру
let deserialized_user: User = serde_json::from_str(&json_string).unwrap();
println!("Десериализовано: {:?}", deserialized_user);
}
Результат выполнения:
JSON: {"name":"Алексей","age":30,"email":"alex@example.com"}
Десериализовано: User { name: "Алексей", age: 30, email: "alex@example.com" }
Работа с вложенными структурами
JSON часто содержит вложенные объекты и массивы. Rust позволяет легко моделировать такие структуры:
#[derive(Debug, Serialize, Deserialize)]
struct Address {
city: String,
street: String,
house: u16,
}
#[derive(Debug, Serialize, Deserialize)]
struct Person {
name: String,
age: u8,
address: Address,
hobbies: Vec<String>,
}
fn main() {
let person = Person {
name: "Мария".to_string(),
age: 25,
address: Address {
city: "Москва".to_string(),
street: "Тверская".to_string(),
house: 10,
},
hobbies: vec!["чтение".to_string(), "спорт".to_string()],
};
// Красивый вывод JSON с отступами
let pretty_json = serde_json::to_string_pretty(&person).unwrap();
println!("{}", pretty_json);
}
Вывод:
{
"name": "Мария",
"age": 25,
"address": {
"city": "Москва",
"street": "Тверская",
"house": 10
},
"hobbies": [
"чтение",
"спорт"
]
}
Обработка ошибок и необязательных полей
В реальных проектах данные могут быть неполными. Rust предлагает элегантные способы обработки таких ситуаций:
- Option<T> — для необязательных полей
- Result<T, E> — для обработки ошибок парсинга
- Атрибуты Serde — для настройки поведения сериализации
use serde::{Serialize, Deserialize};
use serde_json::Result;
#[derive(Debug, Serialize, Deserialize)]
struct Product {
name: String,
price: f64,
#[serde(default)] // Значение по умолчанию, если поле отсутствует
description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")] // Пропустить, если None
discount: Option<f64>,
}
fn parse_product(json_str: &str) -> Result<Product> {
let product: Product = serde_json::from_str(json_str)?;
Ok(product)
}
fn main() {
// JSON без необязательных полей
let json1 = r#"{"name": "Ноутбук", "price": 79999.99}"#;
let product1 = parse_product(json1).unwrap();
println!("{:?}", product1);
// JSON с полным набором полей
let json2 = r#"{"name": "Мышь", "price": 2499.50, "description": "Беспроводная", "discount": 10