serde_json

serializationRustJSONSerdestreaming-api

Serialization Library

serde_json

Overview

serde_json is a fast and flexible JSON serialization and deserialization library for Rust. Integrated with the Serde framework, it provides type-safe conversion between Rust data structures and JSON. It offers comprehensive JSON processing features including streaming API, pretty printing, and dynamic JSON manipulation.

Details

serde_json is the JSON implementation for the Serde framework, efficiently converting strongly-typed Rust data structures to and from JSON. It achieves performance comparable to or better than the fastest C/C++ JSON libraries.

Key Features:

  • High Performance: 500-1000 MB/s deserialization, 600-900 MB/s serialization speeds
  • Streaming API: Efficient processing of large JSON data
  • Pretty Printing: Support for readable formatted output
  • Dynamic JSON Manipulation: Flexible JSON operations via serde_json::Value type
  • Macro Support: Intuitive JSON construction with json! macro

Main Functions:

  • to_string: Serialization to JSON
  • to_string_pretty: Serialization to formatted JSON
  • to_writer: Direct writing to files or network streams
  • from_str: Deserialization from JSON
  • StreamDeserializer: Sequential processing of multiple JSON values

Technical Details:

  • Zero-copy deserialization support
  • Indent control via custom PrettyFormatter
  • no-std environment support (with "alloc" feature)
  • Arbitrary precision number support

Pros and Cons

Pros

  • Industry-leading performance
  • Complete integration with Serde ecosystem
  • Rich feature set (streaming, pretty printing, etc.)
  • Excellent error messages and debugging experience
  • Active maintenance and community support
  • Compliance with standard JSON specification

Cons

  • Initial learning cost due to Serde dependency
  • JSON limitations (e.g., no comments, no key order guarantee)
  • Complex custom serialization implementation
  • Impact on compile time (when using derive)

References

Code Examples

Basic Usage

use serde::{Serialize, Deserialize};
use serde_json;

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

fn main() -> Result<(), serde_json::Error> {
    let person = Person {
        name: "John Doe".to_string(),
        age: 30,
        email: "[email protected]".to_string(),
    };
    
    // Serialize to JSON
    let json = serde_json::to_string(&person)?;
    println!("JSON: {}", json);
    
    // Serialize to pretty JSON
    let pretty_json = serde_json::to_string_pretty(&person)?;
    println!("Pretty JSON:\n{}", pretty_json);
    
    // Deserialize
    let deserialized: Person = serde_json::from_str(&json)?;
    println!("Deserialized: {:?}", deserialized);
    
    Ok(())
}

Using json! Macro

use serde_json::{json, Value};

fn main() {
    // Dynamically construct JSON with json! macro
    let data = json!({
        "name": "Project Config",
        "version": "1.0.0",
        "dependencies": {
            "serde": "1.0",
            "tokio": "1.0"
        },
        "features": ["async", "json", "yaml"],
        "active": true,
        "count": 42
    });
    
    println!("{}", serde_json::to_string_pretty(&data).unwrap());
    
    // Access values
    println!("Name: {}", data["name"]);
    println!("Version: {}", data["version"]);
    
    // Dynamic value modification
    let mut value = data;
    value["count"] = json!(100);
    value["features"].as_array_mut().unwrap().push(json!("new_feature"));
}

Streaming API

use serde::{Serialize, Deserialize};
use serde_json::Deserializer;

#[derive(Serialize, Deserialize, Debug)]
struct LogEntry {
    timestamp: String,
    level: String,
    message: String,
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // String containing multiple JSON values
    let json_stream = r#"
        {"timestamp": "2024-01-01T10:00:00Z", "level": "INFO", "message": "Server started"}
        {"timestamp": "2024-01-01T10:00:01Z", "level": "DEBUG", "message": "Connection established"}
        {"timestamp": "2024-01-01T10:00:02Z", "level": "ERROR", "message": "Failed to process request"}
    "#;
    
    // Create streaming deserializer
    let stream = Deserializer::from_str(json_stream).into_iter::<LogEntry>();
    
    for item in stream {
        match item {
            Ok(entry) => println!("{:?}", entry),
            Err(e) => eprintln!("Error: {}", e),
        }
    }
    
    Ok(())
}

File I/O

use serde::{Serialize, Deserialize};
use std::fs::File;
use std::io::{BufReader, BufWriter};

#[derive(Serialize, Deserialize, Debug)]
struct Config {
    database_url: String,
    port: u16,
    workers: usize,
    features: Vec<String>,
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let config = Config {
        database_url: "postgres://localhost/mydb".to_string(),
        port: 8080,
        workers: 4,
        features: vec!["auth".to_string(), "api".to_string()],
    };
    
    // Write to file
    let file = File::create("config.json")?;
    let writer = BufWriter::new(file);
    serde_json::to_writer_pretty(writer, &config)?;
    
    // Read from file
    let file = File::open("config.json")?;
    let reader = BufReader::new(file);
    let loaded_config: Config = serde_json::from_reader(reader)?;
    
    println!("Loaded config: {:?}", loaded_config);
    
    Ok(())
}

Custom Pretty Printing

use serde_json::{to_string_pretty, Value};
use serde_json::ser::PrettyFormatter;

fn main() -> Result<(), serde_json::Error> {
    let data = serde_json::json!({
        "users": [
            {"id": 1, "name": "Alice"},
            {"id": 2, "name": "Bob"}
        ],
        "settings": {
            "theme": "dark",
            "language": "en"
        }
    });
    
    // Default formatting (2 spaces)
    let pretty = to_string_pretty(&data)?;
    println!("Default formatting:\n{}", pretty);
    
    // Custom formatter (4 spaces)
    let buf = Vec::new();
    let formatter = PrettyFormatter::with_indent(b"    ");
    let mut ser = serde_json::Serializer::with_formatter(buf, formatter);
    data.serialize(&mut ser)?;
    let result = String::from_utf8(ser.into_inner()).unwrap();
    println!("\nCustom formatting (4 spaces):\n{}", result);
    
    Ok(())
}

Error Handling

use serde::{Serialize, Deserialize};
use serde_json::{Error, Result};

#[derive(Serialize, Deserialize, Debug)]
struct User {
    id: u64,
    name: String,
    age: Option<u32>,
}

fn parse_user(json: &str) -> Result<User> {
    serde_json::from_str(json)
}

fn main() {
    // Valid JSON
    let valid_json = r#"{"id": 1, "name": "Alice", "age": 30}"#;
    match parse_user(valid_json) {
        Ok(user) => println!("Parsed user: {:?}", user),
        Err(e) => eprintln!("Error: {}", e),
    }
    
    // Invalid JSON (type mismatch)
    let invalid_json = r#"{"id": "not_a_number", "name": "Bob"}"#;
    match parse_user(invalid_json) {
        Ok(user) => println!("Parsed user: {:?}", user),
        Err(e) => {
            eprintln!("Error: {}", e);
            eprintln!("Line: {}, Column: {}", e.line(), e.column());
        }
    }
    
    // Incomplete JSON
    let incomplete_json = r#"{"id": 1, "name": "Charlie""#;
    if let Err(e) = parse_user(incomplete_json) {
        eprintln!("Parse error: {}", e);
        // Determine error type
        use serde_json::error::Category;
        match e.classify() {
            Category::Io => eprintln!("IO error"),
            Category::Syntax => eprintln!("Syntax error"),
            Category::Data => eprintln!("Data error"),
            Category::Eof => eprintln!("Unexpected end of input"),
        }
    }
}