JSON в Rust для начинающих: парсинг и сериализация данных

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Введение в работу с JSON в Rust

JSON (JavaScript Object Notation) — один из самых популярных форматов обмена данными в веб-разработке, конфигурационных файлах и API. Если вы изучаете Rust и хотите научиться эффективно работать с JSON, эта статья для вас. Мы рассмотрим основные библиотеки, принципы парсинга и сериализации, а также разберём практические примеры кода.

В Rust экосистема для работы с JSON построена вокруг крейта serde — мощного фреймворка для сериализации и десериализации данных. В паре с serde_json он позволяет легко преобразовывать структуры данных в JSON и обратно. Давайте разберёмся, как это работает.

Установка и настройка зависимостей

Для начала создадим новый проект и добавим необходимые зависимости в файл Cargo.toml:

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

Крейт serde предоставляет макросы #[derive(Serialize, Deserialize)], которые автоматически реализуют необходимые типажи для ваших структур. serde_json отвечает непосредственно за работу с JSON-форматом.

Парсинг JSON: из строки в структуру

Допустим, у нас есть JSON-строка с информацией о пользователе. Мы хотим преобразовать её в типизированную структуру Rust. Вот как это делается:

use serde::{Deserialize, Serialize};

#[derive(Debug, Deserialize, Serialize)]
struct User {
    name: String,
    age: u32,
    email: String,
    is_active: bool,
}

fn main() {
    let json_data = r#"
        {
            "name": "Алексей",
            "age": 28,
            "email": "alex@example.com",
            "is_active": true
        }
    "#;

    // Парсинг JSON в структуру
    let user: User = serde_json::from_str(json_data).expect("Ошибка парсинга JSON");
    
    println!("Пользователь: {:?}", user);
    println!("Имя: {}", user.name);
    println!("Возраст: {}", user.age);
}

Обратите внимание на атрибут #[derive(Debug, Deserialize, Serialize)]. Debug позволяет выводить структуру в консоль, а Deserialize и Serialize дают возможность преобразовывать данные в JSON и обратно.

Обработка вложенных объектов

JSON часто содержит вложенные объекты. Рассмотрим пример с адресом пользователя:

#[derive(Debug, Deserialize, Serialize)]
struct Address {
    city: String,
    street: String,
    zip_code: String,
}

#[derive(Debug, Deserialize, Serialize)]
struct User {
    name: String,
    age: u32,
    address: Address,
}

fn main() {
    let json_data = r#"
        {
            "name": "Мария",
            "age": 32,
            "address": {
                "city": "Москва",
                "street": "Тверская, 15",
                "zip_code": "101000"
            }
        }
    "#;

    let user: User = serde_json::from_str(json_data).unwrap();
    println!("Город: {}", user.address.city);
}

Работа с массивами JSON

Массивы в JSON преобразуются в Vec или другие коллекции Rust:

use serde::Deserialize;

#[derive(Debug, Deserialize)]
struct Product {
    id: u32,
    name: String,
    price: f64,
}

fn main() {
    let json_array = r#"
        [
            {"id": 1, "name": "Ноутбук", "price": 75000.50},
            {"id": 2, "name": "Мышь", "price": 1500.00},
            {"id": 3, "name": "Клавиатура", "price": 3500.00}
        ]
    "#;

    let products: Vec<Product> = serde_json::from_str(json_array).unwrap();
    
    for product in &products {
        println!("{}: {} руб.", product.name, product.price);
    }
}

Сериализация: из структуры в JSON

Обратный процесс — преобразование структуры Rust в JSON-строку. Это называется сериализацией:

use serde::Serialize;

#[derive(Debug, Serialize)]
struct Config {
    host: String,
    port: u16,
    debug: bool,
    allowed_origins: Vec<String>,
}

fn main() {
    let config = Config {
        host: String::from("127.0.0.1"),
        port: 8080,
        debug: true,
        allowed_origins: vec![
            String::from("http://localhost:3000"),
            String::from("https://myapp.com"),
        ],
    };

    // Красивый вывод с отступами
    let json_pretty = serde_json::to_string_pretty(&config).unwrap();
    println!("{}", json_pretty);
    
    // Компактный вывод без пробелов
    let json_compact = serde_json::to_string(&config).unwrap();
    println!("{}", json_compact);
}

Метод to_string_pretty создаёт читаемый JSON с отступами, а to_string — компактную строку для передачи по сети.

Продвинутые техники работы с JSON

Изменение имён полей

Иногда имена полей в JSON отличаются от имён переменных в Rust. Используйте атрибут #[serde(rename)]:

#[derive(Debug, Deserialize, Serialize)]
struct ApiResponse {
    #[serde(rename = "user_id")]
    user_id: u32,
    #[serde(rename = "full_name")]
    full_name: String,
    #[serde(rename = "created_at")]
    created_at: String,
}

Опциональные поля

Не все поля JSON могут присутствовать. Используйте Option для обработки таких случаев:

#[derive(Debug, Deserialize)]
struct User {
    name: String,
    age: u32,
    #[serde(default)]
    email: Option<String>,  // Поле может отсутствовать
    #[serde(default = "default_phone")]
    phone: String,
}

fn default_phone() -> String {
    String::from("Не указан")
}

Работа с произвольным JSON

Если структура JSON заранее неизвестна, используйте тип serde_json::Value:

use serde_json::Value;

fn main() {
    let data = r#"
        {
            "name": "Тест",
            "values": [1, 2, 3],
            "nested": {"key": "value"},
            "flag": true
        }
    "#;

    let v: Value = serde_json::from_str(data).unwrap();                

Блоги

Рекомендаций по книгам