← Back to Blog

Rust JSON with serde: Serialize, Deserialize, and Transform

April 2, 2026 3 min read By CodeTidy Team

The JSON Dilemma: How Rust's serde Saves the Day

Have you ever found yourself wrestling with JSON data in Rust, only to end up with a mess of string manipulation and error handling? You're not alone. We've all been there. But fear not, dear Rustaceans, for serde is here to save the day.

Table of Contents

  • Getting Started with serde_json
  • Derive Macros: The Easy Way to Serialize and Deserialize
  • Dynamic JSON with the Value Type
  • Custom Serializers: When You Need More Control
  • Transforming JSON Data with Ease
  • Putting it all Together: A Real-World Example

Getting Started with serde_json

serde_json is a popular Rust library that provides a simple and efficient way to work with JSON data. At its core, serde_json provides two main features: serialization and deserialization. Serialization is the process of converting Rust data structures into JSON, while deserialization is the reverse process of converting JSON into Rust data structures.

To get started with serde_json, add the following dependency to your Cargo.toml file:

[dependencies]
serde_json = "1.0"

Derive Macros: The Easy Way to Serialize and Deserialize

One of the most powerful features of serde is its derive macros. With a simple #[derive(Serialize, Deserialize)] attribute, you can automatically generate serialization and deserialization code for your Rust data structures.

Here's an example of a simple struct that uses derive macros:

use serde::{Serialize, Deserialize};

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

fn main() {
    let person = Person {
        name: String::from("John"),
        age: 30,
    };

    let json = serde_json::to_string(&person).unwrap();
    println!("{}", json); // Output: {"name":"John","age":30}
}

Dynamic JSON with the Value Type

Sometimes, you need to work with JSON data that doesn't fit into a predefined schema. That's where the Value type comes in. Value is a dynamic JSON type that can represent any valid JSON value.

Here's an example of using the Value type to parse a JSON string:

use serde_json::Value;

fn main() {
    let json = r#"
        {
            "name": "John",
            "age": 30,
            "address": {
                "street": "123 Main St",
                "city": "Anytown",
                "state": "CA",
                "zip": "12345"
            }
        }
    "#;

    let value: Value = serde_json::from_str(json).unwrap();
    println!("{}", value["name"]); // Output: "John"
}

Custom Serializers: When You Need More Control

While derive macros are convenient, sometimes you need more control over the serialization process. That's where custom serializers come in. Custom serializers allow you to implement your own serialization logic for specific types.

Here's an example of a custom serializer for a DateTime type:

use serde::{Serialize, Serializer};
use chrono::DateTime;

struct DateTimeSerializer(DateTime);

impl Serialize for DateTimeSerializer {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        serializer.serialize_str(&self.0.to_rfc3339())
    }
}

fn main() {
    let dt = DateTime::parse_from_rfc3339("2022-07-25T14:30:00Z").unwrap();
    let serializer = DateTimeSerializer(dt);
    let json = serde_json::to_string(&serializer).unwrap();
    println!("{}", json); // Output: "2022-07-25T14:30:00Z"
}

Transforming JSON Data with Ease

One of the most powerful features of serde is its ability to transform JSON data. With serde's transform module, you can easily transform JSON data from one format to another.

Here's an example of transforming a JSON object to a JSON array:

use serde_json::{Value, transform};

fn main() {
    let json = r#"
        {
            "name": "John",
            "age": 30
        }
    "#;

    let value: Value = serde_json::from_str(json).unwrap();
    let transformed = transform::array(value);
    println!("{}", transformed); // Output: [{"name":"John","age":30}]
}

Putting it all Together: A Real-World Example

Let's say we're building a RESTful API that returns a list of users. We can use serde to serialize and deserialize the user data.

Here's an example of how we might use serde to serialize and deserialize user data:

use serde::{Serialize, Deserialize};

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

fn main() {
    let users = vec![
        User {
            name: String::from("John"),
            age: 30,
        },
        User {
            name: String::from("Jane"),
            age: 25,
        },
    ];

    let json = serde_json::to_string(&users).unwrap();
    println!("{}", json); // Output: [{"name":"John","age":30},{"name":"Jane","age":25}]

    let deserialized: Vec<User> = serde_json::from_str(&json).unwrap();
    println!("{:?}", deserialized); // Output: [User { name: "John", age: 30 }, User { name: "Jane", age: 25 }]
}

Key Takeaways

  • Use serde's derive macros to automatically generate serialization and deserialization code for your Rust data structures.
  • Use the Value type to work with dynamic JSON data.
  • Implement custom serializers when you need more control over the serialization process.
  • Use serde's transform module to transform JSON data from one format to another.
  • Use serde to serialize and deserialize data in real-world applications.

FAQ

Q: What is the difference between Serialize and Deserialize?

A: Serialize is used to convert Rust data structures into JSON, while Deserialize is used to convert JSON into Rust data structures.

Q: How do I use custom serializers?

A: To use custom serializers, implement the Serialize trait for your type and provide a custom serialization implementation.

Q: Can I use serde with other Rust libraries?

A: Yes, serde can be used with other Rust libraries, such as Rocket and actix-web, to serialize and deserialize data.

AI agent tools available. The CodeTidy MCP Server gives Claude, Cursor, and other AI agents access to 60+ developer tools. One command: npx @codetidy/mcp