serde

repository·master·Indexed 27 days ago

https://github.com/serde-rs/serde

A generic serialization/deserialization framework for Rust that allows data structures to be converted to and from various data formats efficiently. It provides core traits like Serialize and Deserialize, with optional derive macros for automatic implementation. While the core framework is provided by the serde and serde_core crates, specific data formats (such as JSON, YAML, and TOML) are implemented in separate crates.

Tokens
4K
Snippets
6
Records
22
Agent score
95%

What's inside serde

  1. Install Serde and enable derive macros

    master

    To use Serde, add the serde crate to your Cargo.toml. To use the #[derive(Serialize, Deserialize)] macros on your structs and enums, you must enable the derive feature.

    Note that Serde itself provides the core traits, but data formats (like JSON, YAML, etc.) live in separate crates. For example, to work with JSON, you must also include serde_json.

    [dependencies]
    # The core APIs, including the Serialize and Deserialize traits. Always
    # required when using Serde. The "derive" feature is only required when
    # using #[derive(Serialize, Deserialize)] to make Serde work with structs
    # and enums defined in your crate.
    serde = { version = "1.0", features = ["derive"] }
    
    # Each data format lives in its own crate; the sample code below uses JSON
    # but you may be using a different one.
    serde_json = "1.0"
  2. Choose between `serde` and `serde_core` for dependencies

    master

    When deciding which crate to depend on, follow these guidelines based on your use case:

    1. If you need to use #[derive(Serialize, Deserialize)]: You must depend on the serde crate. The serde_core crate does not support the derive() functionality.

    2. If you are handwriting trait implementations or using trait bounds: You can depend on serde_core. However, since serde re-exports all traits from serde_core, it is generally recommended to use serde to avoid confusion.

    3. If you want to optimize build times: Depending on serde_core instead of serde allows your crate to compile in parallel with serde_derive (even when serde's "derive" feature is enabled).

  3. Serialize and Deserialize Rust data structures with Serde

    master

    Serde is a framework for efficiently and generically serializing and deserializing Rust data structures. To use it with your own structs and enums, you typically use the Serialize and Deserialize traits. When using the derive feature, you can automatically implement these traits using #[derive(Serialize, Deserialize)].

    Note that Serde itself provides the framework, while specific data formats (like JSON, YAML, or TOML) are provided by separate crates (e.g., serde_json).

    use serde::{Deserialize, Serialize};
    
    #[derive(Serialize, Deserialize, Debug)]
    struct Point {
        x: i32,
        y: i32,
    }
    
    fn main() {
        let point = Point { x: 1, y: 2 };
    
        // Convert the Point to a JSON string.
        let serialized = serde_json::to_string(&point).unwrap();
    
        // Prints serialized = {"x":1,"y":2}
        println!("serialized = {}", serialized);
    
        // Convert the JSON string back to a Point.
        let deserialized: Point = serde_json::from_str(&serialized).unwrap();
    
        // Prints deserialized = Point { x: 1, y: 2 }
        println!("deserialized = {:?}", deserialized);
    }
  4. Understand the Serde framework design

    master
    Serde is a framework for serializing and deserializing Rust data structures efficiently and generically. It works by separating data structures (which implement Serialize and Deserialize traits) from data formats (which implement Serializer and Deserializer traits). This decoupling allows any supported data structure to be used with any supported data format without runtime reflection overhead, as the interaction is resolved at compile time via Rust's trait system.
  5. Enable automatic serialization/deserialization with derive

    master
    If the serde_derive feature is enabled, you can use the #[derive(Serialize, Deserialize)] attribute to automatically generate the necessary trait implementations for your structs and enums at compile time. This is the most common way to use Serde.
  6. Deserialize basic values using IntoDeserializer

    master

    The IntoDeserializer trait allows you to convert basic Rust types directly into deserializers. This is useful for implementing FromStr or other conversion traits where you want to leverage Serde's existing Deserialize implementations for your types.

    Example of implementing FromStr for a custom enum using IntoDeserializer:

    use serde::de::{value, Deserialize, IntoDeserializer};
    use serde_derive::Deserialize;
    use std::str::FromStr;
    
    #[derive(Deserialize)]
    enum Setting {
        On,
        Off,
    }
    
    impl FromStr for Setting {
        type Err = value::Error;
    
        fn from_str(s: &str) -> Result<Self, Self::Err> {
            Self::deserialize(s.into_deserializer())
        }
    }
    use serde::de::{value, Deserialize, IntoDeserializer};
    use serde_derive::Deserialize;
    use std::str::FromStr;
    
    #[derive(Deserialize)]
    enum Setting {
        On,
        Off,
    }
    
    impl FromStr for Setting {
        type Err = value::Error;
    
        fn from_str(s: &str) -> Result<Self, Self::Err> {
            Self::deserialize(s.into_deserializer())
        }
    }
  7. Choose between `serde` and `serde_core`

    master

    When working with Serde, you must choose the correct crate based on your use case:

    • Use serde if you want to use #[derive(Serialize, Deserialize)] on your structs and enums. This is the standard choice for most users.
    • Use serde_core only if you are hand-writing custom implementations of Serde traits or using them strictly as trait bounds. serde_core contains the core trait definitions but does not support the #[derive] macros.

    Note: If you attempt to use Serde's derive macros while depending only on serde_core, the compilation will fail with a compile error.

  8. Serialize and Deserialize data structures

    master

    You can use Serde to convert Rust data structures to and from various data formats. By deriving Serialize and Deserialize, your types become compatible with any Serde-supported format.

    In this example, we use serde_json to convert a Point struct to a JSON string and back again.

    use serde::{Deserialize, Serialize};
    
    #[derive(Serialize, Deserialize, Debug)]
    struct Point {
        x: i32,
        y: i32,
    }
    
    fn main() {
        let point = Point { x: 1, y: 2 };
    
        // Convert the Point to a JSON string.
        let serialized = serde_json::to_string(&point).unwrap();
    
        // Prints serialized = {"x":1,"y":2}
        println!("serialized = {}", serialized);
    
        // Convert the JSON string back to a Point.
        let deserialized: Point = serde_json::from_str(&serialized).unwrap();
    
        // Prints deserialized = Point { x: 1, y: 2 }
        println!("deserialized = {:?}", deserialized);
    }
  9. Derive Serialize and Deserialize traits

    master

    Use the Serialize and Deserialize procedural macros to automatically implement Serde's serialization and deserialization traits for your structs and enums. These macros support custom configuration via #[serde] attributes.

    To use them, you typically need to include serde_derive in your dependencies and use the #[derive(...)] syntax on your data types.

    use serde_derive::{Deserialize, Serialize};
    
    #[derive(Serialize, Deserialize)]
    struct S;
    
    fn main() {}
  10. Use Serialize and Deserialize traits

    master
    To make a Rust data structure compatible with Serde, it must implement the Serialize and Deserialize traits. These traits are the core of the framework and are required for any data structure to interact with a data format.
  11. Discard data using `IgnoredAny`

    master

    Use the IgnoredAny type to efficiently discard data from a deserializer without storing any information about the deserialized values. It is similar to serde_json::Value in that it can be deserialized from any type, but it consumes no memory for the data itself. This is useful when you want to skip specific parts of a data stream (like elements in a sequence or entries in a map) while only capturing specific fields.

    use serde::de::{self, Deserialize, DeserializeSeed, Deserializer, IgnoredAny, SeqAccess, Visitor};
    use std::fmt;
    use std::marker::PhantomData;
    
    /// A seed that can be used to deserialize only the `n`th element of a sequence
    /// while efficiently discarding elements of any type before or after index `n`.
    ///
    /// For example to deserialize only the element at index 3:
    ///
    /// ```
    /// NthElement::new(3).deserialize(deserializer)
    /// ```
    pub struct NthElement<T> {
        n: usize,
        marker: PhantomData<T>,
    }
    
    impl<T> NthElement<T> {
        pub fn new(n: usize) -> Self {
            NthElement {
                n,
                marker: PhantomData,
            }
        }
    }
    
    impl<'de, T> Visitor<'de> for NthElement<T>
    where
        T: Deserialize<'de>,
    {
        type Value = T;
    
        fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
            write!(
                formatter,
                "a sequence in which we care about element {}",
                self.n
            )
        }
    
        fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
        where
            A: SeqAccess<'de>,
        {
            // Skip over the first `n` elements.
            for i in 0..self.n {
                // It is an error if the sequence ends before we get to element `n`.
                if seq.next_element::<IgnoredAny>()?.is_none() {
                    return Err(de::Error::invalid_length(i, &self));
                }
            }
    
            // Deserialize the one we care about.
            let nth = match seq.next_element()? {
                Some(nth) => nth,
                None => {
                    return Err(de::Error::invalid_length(self.n, &self));
                }
            };
    
            // Skip over any remaining elements in the sequence after `n`.
            while let Some(IgnoredAny) = seq.next_element()? {
                // ignore
            }
    
            Ok(nth)
        }
    }
    
    impl<'de, T> DeserializeSeed<'de> for NthElement<T>
    where
        T: Deserialize<'de>,
    {
        type Value = T;
    
        fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
        where
            D: Deserializer<'de>,
        {
            deserializer.deserialize_seq(self)
        }
    }