Working with JSON in Rust
Rust uses the serde and serde_json crates for JSON serialization.
These crates provide zero-cost abstractions and excellent performance for JSON handling.
Setup
Add these dependencies to your Cargo.toml:
[dependencies]
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0" Deserializing JSON
Use serde_json::from_str() to parse JSON into Rust types:
use serde::{Deserialize, Serialize};
use serde_json;
#[derive(Debug, Deserialize)]
struct User {
name: String,
age: u32,
email: String,
#[serde(default)]
is_active: bool,
}
fn main() -> Result<(), serde_json::Error> {
let json_str = r#"
{
"name": "John",
"age": 30,
"email": "john@example.com",
"is_active": true
}
"#;
let user: User = serde_json::from_str(json_str)?;
println!("Name: {}", user.name);
println!("Age: {}", user.age);
println!("Active: {}", user.is_active);
Ok(())
} Serializing to JSON
Use serde_json::to_string() to convert Rust values to JSON:
use serde::{Deserialize, Serialize};
use serde_json;
#[derive(Debug, Serialize)]
struct User {
name: String,
age: u32,
hobbies: Vec,
is_active: bool,
}
fn main() -> Result<(), serde_json::Error> {
let user = User {
name: String::from("Jane"),
age: 25,
hobbies: vec![String::from("reading"), String::from("coding")],
is_active: true,
};
// Compact JSON
let json = serde_json::to_string(&user)?;
println!("{}", json);
// Pretty print
let pretty_json = serde_json::to_string_pretty(&user)?;
println!("{}", pretty_json);
Ok(())
} JSON Validation
use serde_json::{self, Value};
fn is_valid_json(s: &str) -> bool {
serde_json::from_str::(s).is_ok()
}
struct ValidationResult {
valid: bool,
error: Option,
json_type: Option,
}
fn validate_json(s: &str) -> ValidationResult {
match serde_json::from_str::(s) {
Ok(value) => {
let json_type = match &value {
Value::Object(_) => "object",
Value::Array(_) => "array",
Value::String(_) => "string",
Value::Number(_) => "number",
Value::Bool(_) => "boolean",
Value::Null => "null",
};
ValidationResult {
valid: true,
error: None,
json_type: Some(json_type.to_string()),
}
}
Err(e) => ValidationResult {
valid: false,
error: Some(e.to_string()),
json_type: None,
},
}
}
fn main() {
println!("{}", is_valid_json(r#"{"name": "John"}"#)); // true
println!("{}", is_valid_json("{invalid}")); // false
} Serde Attributes
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
struct User {
// Rename field in JSON
#[serde(rename = "user_name")]
name: String,
// Skip serializing
#[serde(skip_serializing)]
password: String,
// Skip if None
#[serde(skip_serializing_if = "Option::is_none")]
middle_name: Option,
// Default value
#[serde(default)]
score: i32,
} Best Practices
- Use
#[derive(Serialize, Deserialize)]for automatic implementation - Use
serde_json::Valuefor dynamic JSON when structure is unknown - Use
json!macro for creating JSON on the fly - Prefer
from_reader/to_writerfor file operations - Use
Option<T>for optional fields withskip_serializing_if - Implement custom serializers for complex types
- Use streaming deserialization for large JSON files
- Handle errors explicitly with
Resulttypes