Introduction: Why is JSON Important in Rust?
JSON (JavaScript Object Notation) remains one of the most popular formats for data exchange between services, configuration files, and APIs. In the Rust ecosystem, working with JSON is implemented through the powerful serde_json crate, which, together with the serde framework, provides an elegant and safe way to transform data structures.
Unlike dynamic languages, Rust requires explicit type definitions, which may seem complicated at first glance. However, in practice, this provides incredible reliability: you will never encounter an unexpected null or a field with an incorrect type at runtime. In this article, we will cover JSON parsing (deserialization) and serialization using concrete examples that can be immediately applied in real projects.
1. Project Setup and Dependency Installation
Before writing code, let's add the necessary crates to Cargo.toml:
[dependencies]serde = { version = "1.0", features = ["derive"] }serde_json = "1.0"The serde crate with the derive flag allows automatic code generation for serialization and deserialization of our structures. serde_json is directly responsible for converting to/from JSON.
Let's import everything needed in main.rs:
use serde::{Deserialize, Serialize};use serde_json::Result;2. Serialization: Converting a Rust Structure to JSON
Serialization is the process of converting a Rust object into a JSON string. Let's look at a classic example with user data.
Declaring the Structure
Let's add the #[derive(Serialize, Deserialize)] annotations to our structure:
#[derive(Debug, Serialize, Deserialize)]struct User { id: u32, name: String, email: String, is_active: bool, tags: Vec<String>,}Creating an Instance and Serialization
Now let's create an object and convert it to JSON:
fn main() -> Result<()> { let user = User { id: 42, name: "Alexey".to_string(), email: "alex@example.com".to_string(), is_active: true, tags: vec!["rust".to_string(), "backend".to_string()], };
// Serialization to a JSON string let json_string = serde_json::to_string(&user)?; println!("{}", json_string);
// Pretty serialization let pretty_json = serde_json::to_string_pretty(&user)?; println!("{}", pretty_json);
Ok(())}Execution result:
{"id":42,"name":"Alexey","email":"alex@example.com","is_active":true,"tags":["rust","backend"]}
{ "id": 42, "name": "Alexey", "email": "alex@example.com", "is_active": true, "tags": [ "rust", "backend" ]}Note: to_string_pretty adds indentation and line breaks — perfect for configuration files or logs.
3. Parsing (Deserialization): From JSON to a Rust Structure
Deserialization is the reverse process: we receive a JSON string and convert it into a typed Rust structure.
Parsing from a String
fn parse_user_from_json(json_str: &str) -> Result<User> { let user: User = serde_json::from_str(json_str)?; Ok(user)}
fn main() -> Result<()> { let data = r#" { "id": 101, "name": "Maria", "email": "maria@example.com", "is_active": false, "tags": ["python", "data-science"] } "#;
let user = parse_user_from_json(data)?; println!("User: {:?}", user); println!("Name: {}", user.name); println!("Active: {}", user.is_active);
Ok(())}If the JSON does not match the structure (e.g.