TL;DR:
serde_jsonis the standard crate for JSON in Rust; built on top ofserde. Useserde_json::to_string(&value)to serialize,serde_json::from_str::<T>(json)to deserialize, andserde_json::Valuefor dynamic JSON without a known schema. Add#[derive(Serialize, Deserialize)]to your structs and they work withserde_jsonautomatically. It is the most downloaded crate in the Rust ecosystem afterserdeitself.
How Do You Serialize a Struct to JSON?
Add #[derive(Serialize)] and call serde_json::to_string() or serde_json::to_string_pretty().
# Cargo.toml
[dependencies]
serde = { version = "1", features = ["derive"] }
serde_json = "1"use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize, Debug)]
struct User {
id: u64,
name: String,
email: String,
active: bool,
}
fn main() {
let user = User {
id: 1,
name: "Alice".to_string(),
email: "[email protected]".to_string(),
active: true,
};
// Compact JSON
let json = serde_json::to_string(&user).unwrap();
println!("{json}");
// {"id":1,"name":"Alice","email":"[email protected]","active":true}
// Pretty-printed JSON
let pretty = serde_json::to_string_pretty(&user).unwrap();
println!("{pretty}");
}How Do You Deserialize JSON Into a Struct?
Use serde_json::from_str() with a type annotation; Rust infers which type to deserialize into.
use serde::Deserialize;
#[derive(Deserialize, Debug)]
struct Config {
host: String,
port: u16,
debug: bool,
}
fn main() {
let json = r#"{"host":"localhost","port":8080,"debug":true}"#;
let config: Config = serde_json::from_str(json).unwrap();
println!("{:?}", config);
// Config { host: "localhost", port: 8080, debug: true }
// From a file
let file = std::fs::File::open("config.json").unwrap();
let config: Config = serde_json::from_reader(file).unwrap();
}How Do You Work With Dynamic JSON (Unknown Schema)?
Use serde_json::Value; an enum that represents any valid JSON value.
use serde_json::{Value, json};
fn main() {
// Build JSON with the json! macro
let payload = json!({
"name": "Bob",
"scores": [10, 20, 30],
"meta": {
"active": true,
"tags": ["rust", "backend"]
}
});
// Navigate the value
println!("{}", payload["name"]); // "Bob"
println!("{}", payload["scores"][1]); // 20
println!("{}", payload["meta"]["active"]); // true
// Parse unknown JSON
let raw = r#"{"status":"ok","count":42}"#;
let v: Value = serde_json::from_str(raw).unwrap();
if let Some(count) = v["count"].as_u64() {
println!("count = {count}");
}
}How Do You Handle Serialization Errors?
serde_json functions return Result; handle errors with ? in fallible contexts.
use serde_json::Result;
fn serialize_user(user: &User) -> Result<String> {
serde_json::to_string(user)
}
fn parse_config(json: &str) -> anyhow::Result<Config> {
let config: Config = serde_json::from_str(json)
.context("failed to parse config JSON")?;
Ok(config)
}Common errors:
from_str; invalid JSON syntax, wrong type for a fieldto_string; rare; can fail if a type contains a non-string map key or a custom serializer errors
What Are the Key serde_json Functions?
| Function | Input | Output |
|---|---|---|
to_string(&T) | Any Serialize | Result<String> |
to_string_pretty(&T) | Any Serialize | Result<String> |
to_vec(&T) | Any Serialize | Result<Vec<u8>> |
to_writer(w, &T) | Writer + Serialize | Result<()> |
from_str::<T>(s) | &str | Result<T> |
from_slice::<T>(b) | &[u8] | Result<T> |
from_reader::<T>(r) | Read impl | Result<T> |
from_value::<T>(v) | Value | Result<T> |
to_value(&T) | Any Serialize | Result<Value> |
Frequently Asked Questions
Use #[serde(rename = "fieldName")] on struct fields or #[serde(rename_all = "camelCase")] on the struct:
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
struct Response {
user_id: u64, // serialized as "userId"
created_at: String, // serialized as "createdAt"
}#[serde(skip_serializing_if = "Option::is_none")]
optional_field: Option<String>,Value is dynamic; you can inspect or modify arbitrary JSON at runtime. Typed structs are validated at parse time and are safer for production code. Use Value for prototyping, proxying, or when the schema is genuinely unknown.
serde_json::to_string_pretty(&value) adds indentation. For custom indentation, use the PrettyFormatter directly via serde_json::ser::Serializer.
Sources
Related Glossary Terms
- Serde: The serialization framework serde_json is built on
- Struct: Structs are the primary target for JSON deserialization
- Result: All serde_json operations return Result
- Axum: Uses serde_json internally for
Json<T>extractors - HashMap: JSON objects are often represented as map-like key-value structures
- Reqwest: Reqwest and serde_json are the default pair for Rust HTTP APIs
- Tauri: Tauri command payloads and frontend messages often flow through JSON values
Keep Reading
- Rust Serde Guide: serde_json is the most common serde format
- Building AI Agents in Rust: JSON parsing for LLM API responses
- Rust on AWS Lambda: JSON request/response handling in serverless Rust
