Working with JSON in Rust: Parsing and Data Serialization

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course
```html

Introduction: Why JSON is Important for Rust Developers

JSON (JavaScript Object Notation) remains one of the most popular data exchange formats in web development, microservices, and APIs. For the Rust language, which is rapidly gaining popularity due to its performance and safety, working with JSON is a critical task. Whether you are a backend developer using Actix or Rocket, writing a CLI utility, or working with web sockets — the ability to parse and serialize JSON in Rust will definitely come in handy.

In this article, we will break down how to efficiently work with JSON in Rust using the standard ecosystem of crates serde and serde_json. You will learn how to convert data structures to and from JSON, handle errors, and work with dynamic data.

Basics: Installing and Adding Dependencies

To work with JSON in Rust, the powerful serialization framework Serde and its JSON implementation — the serde_json crate — are used. To get started, add them to your Cargo.toml:

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

The features = ["derive"] flag allows using the #[derive(Serialize, Deserialize)] macros, which significantly simplifies the code.

Basic JSON String Parsing

The simplest way to start is to parse a JSON string into the universal type serde_json::Value. This is analogous to serde_json::Value in Python or object in JavaScript:

use serde_json::Value;

fn main() {
    let data = r#"
        {
            "name": "Rust",
            "year": 2015,
            "features": ["safe", "fast", "concurrent"]
        }"#;

    let parsed: Value = serde_json::from_str(data).expect("JSON parsing error");
    
    println!("Name: {}", parsed["name"]);
    println!("Year: {}", parsed["year"]);
    println!("First feature: {}", parsed["features"][0]);
}

The Value type can be: Null, Bool, Number, String, Array, or Object. This is convenient for prototyping, but for production code it is better to use strict typing.

Serialization and Deserialization of Structs

The main strength of Serde is the automatic conversion between Rust structs and JSON. Let's look at an example:

use serde::{Serialize, Deserialize};

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

fn main() {
    // Deserialization (JSON -> Rust)
    let json_str = r#"{"id":1,"name":"Alexey","email":"alex@example.com","active":true}"#;
    let user: User = serde_json::from_str(json_str).unwrap();
    println!("User: {:?}", user);

    // Serialization (Rust -> JSON)
    let new_user = User {
        id: 2,
        name: String::from("Maria"),
        email: String::from("maria@example.com"),
        active: false,
    };
    let json_output = serde_json::to_string_pretty(&new_user).unwrap();
    println!("JSON:{}", json_output);
}

Note to_string_pretty — it formats JSON with indentation for readability. For production, use to_string (without extra whitespace).

Working with Nested Structures

JSON often contains nested objects and arrays. Serde handles this easily:

#[derive(Serialize, Deserialize)]
struct Project {
    name: String,
    languages: Vec<String>,
    contributors: Vec<Contributor>,
}

#[derive(Serialize, Deserialize)]
struct Contributor {
    username: String,
    contributions: u32,
}

fn main() {
    let project_json = r#"
    {
        "name": "Serde",
        "languages": ["Rust", "Python", "C++"],
        "contributors": [
```                

Blogs

Book Recommendations