Use the serde_json crate alongside serde to serialize Rust structs into JSON strings by deriving the Serialize trait on your struct definitions. You simply call serde_json::to_string() (or to_string_pretty() for formatted output) passing your struct instance, which returns a Result you must handle.
First, add the dependencies to your Cargo.toml:
[dependencies]
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
Then, define your struct with the #[derive(Serialize)] attribute and serialize it in your code:
use serde::Serialize;
use serde_json;
#[derive(Serialize)]
struct User {
id: u32,
name: String,
active: bool,
}
fn main() {
let user = User {
id: 42,
name: "Alice".to_string(),
active: true,
};
// Compact JSON
let json = serde_json::to_string(&user).expect("Failed to serialize");
println!("{}", json);
// Output: {"id":42,"name":"Alice","active":true}
// Pretty-printed JSON
let pretty_json = serde_json::to_string_pretty(&user).expect("Failed to serialize");
println!("{}", pretty_json);
}
If you need to customize field names or exclude fields, use the #[serde(rename = "...")] or #[serde(skip)] attributes on the struct fields. For example, #[serde(rename = "user_id")] will output the id field as user_id in the JSON. If you are working with optional fields, use Option<T>; serde automatically omits None values unless you explicitly configure it otherwise with #[serde(skip_serializing_if = "Option::is_none")].
Remember that to_string returns a Result<String, Error>, so always handle the error case in production code, typically using ? in functions that return Result or .expect() in main for quick prototyping. This approach works for nested structs, vectors, and maps without additional boilerplate, provided all contained types also implement Serialize.