Rust JSON with Serde
JSON is a text format for data. Rust has no JSON support in the standard library, so the serde and serde_json crates read and write JSON in Rust.
A struct becomes JSON text
Derive Serialize and to_string turns the struct into one compact line of JSON.
#[derive(Serialize)]
struct Person { name: String, age: u32 }
let ada = Person { name: String::from("Ada"), age: 36 };
let text = serde_json::to_string(&ada).unwrap();
println!("{text}");JSON text becomes a struct again
from_str needs the same struct with Deserialize added to its derive list.
let text = "{\"name\":\"Ada\",\"age\":36}";
let ada: Person = serde_json::from_str(text).unwrap();
println!("{} is {}", ada.name, ada.age);Pretty JSON spreads over lines
to_string_pretty is the same content, arranged for human eyes.
let text = serde_json::to_string_pretty(&ada).unwrap();
println!("{text}");No struct? Use a Value
Value holds any JSON shape. Index it like the object it holds.
let v: serde_json::Value = serde_json::from_str("{\"city\":\"Paris\",\"temp\":21.5}").unwrap();
println!("city = {}", v["city"]);
println!("temp = {}", v["temp"].as_f64().unwrap());Build JSON with the json! macro
json! writes the JSON shape right inside your Rust code.
let v = serde_json::json!({ "name": "Ada", "tags": ["math", "code"] });
println!("{}", v["tags"][1].as_str().unwrap());Example
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
struct Person {
name: String,
age: u32,
}
fn main() {
let team = vec![
Person { name: String::from("Ada"), age: 36 },
Person { name: String::from("Grace"), age: 45 },
];
let text = serde_json::to_string_pretty(&team).unwrap();
println!("{text}");
let back: Vec<Person> = serde_json::from_str(&text).unwrap();
println!("people = {}", back.len());
println!("first = {} ({})", back[0].name, back[0].age);
}
Key points
- Serde is the framework.
- serde_json is its JSON plug-in.
- serde_json::to_string writes your struct as a JSON string.
- serde_json::from_str reads a string back into it.
- Both return a Result.
- Add the crates in Cargo.toml.
- The file gets serde = { version = "1", features = ["derive"] } and serde_json = "1".
- The derive feature writes the Serialize and Deserialize code for your struct.
- The Derivable Traits lesson covers the derive idea.
- Use serde_json::to_string_pretty when a person will read the text.
- It spreads the JSON over several lines with spaces.
- And from_str accepts it back just the same.
- Use the serde_json::Value type when there is no struct to fill.
- It holds any JSON: a number, a string, an array.
- And v["city"] reaches a named piece of an object.
- Use the json! macro to build a Value by hand, piece by piece, like writing the JSON directly in your program.
- A struct field missing from the text gives an Err from from_str.
- Text with the wrong shape for a field gives one too.
- You never get a half-filled struct.
- APIs send JSON, and config files use it.
- JSON carries data as text.
The closing program builds a small team. It prints it as pretty JSON. Then it reads the text back into Rust structs. Last, it checks the round trip. Two people come back. The first is Ada (36).
Note
Reading is strict.
Try it Yourself
Edit the code, then press Run.
Running is simulated · Rust Playground
Exercise
Your struct has a field the JSON text never mentions, and the field has no default. What does from_str return?