#serde-yaml #serialization #serde #serde-derive

serde_yml

A robust Rust library that simplifies the serialization and deserialization of Rust data structures to and from YAML format using the widely-used Serde framework

7 releases

new 0.0.7 May 14, 2024
0.0.6 May 12, 2024
0.0.4 Apr 3, 2024
0.0.2 Mar 29, 2024

#111 in Encoding

Download history 526/week @ 2024-03-28 248/week @ 2024-04-04 169/week @ 2024-04-11 689/week @ 2024-04-18 484/week @ 2024-04-25 879/week @ 2024-05-02 599/week @ 2024-05-09

2,677 downloads per month
Used in 8 crates (7 directly)

MIT/Apache

465KB
11K SLoC

Serde YML logo

Serde YML: Seamless YAML Serialization for Rust

Serde YML is a Rust library that simplifies YAML serialization and deserialization using the popular Serde framework. It provides a convenient and efficient way to convert Rust data structures to YAML format and vice versa.

Credits and Acknowledgements

This new library draws inspiration from the excellent work done by David Tolnay and the maintainers of the serde-yaml library. While Serde YML started as a fork of serde-yaml, it has now evolved into a separate library with its own goals and direction in mind.

If you are currently using serde-yaml in your projects, we recommend carefully evaluating your requirements and considering the stability and maturity of the original library before migrating to Serde YML.

Finally, I would like to express my sincere gratitude to David Tolnay and the serde-yaml team for their valuable contributions to the Rust community and for inspiring this project.

divider

Banner of Serde YML

Made With Rust Crates.io Lib.rs Docs.rs License Codecov

WebsiteDocumentationReport BugRequest FeatureContributing Guidelines

Features

  • Serialization and deserialization of Rust data structures to/from YAML format
  • Support for custom structs and enums using Serde's derive macros
  • Handling of YAML's !tag syntax for representing enum variants
  • Direct access to YAML values through the Value type and related types like Mapping and Sequence
  • Comprehensive error handling with Error, Location, and Result types
  • Serialization to YAML using to_string and to_writer functions
  • Deserialization from YAML using from_str, from_slice, and from_reader functions
  • Customizable serialization and deserialization behavior using Serde's #[serde(with = ...)] attribute
  • Support for serializing/deserializing enums using a YAML map with a single key-value pair through the singleton_map module
  • Recursive application of singleton_map serialization/deserialization to all enums within a data structure using the singleton_map_recursive module
  • Serialization and deserialization of optional enum fields using the singleton_map_optional module
  • Handling of nested enum structures with optional inner enums using the singleton_map_recursive module
  • Customization of serialization and deserialization logic for enums using the singleton_map_with module and custom helper functions

Rust Version Compatibility

This library is compatible with Rust 1.60 and above.

Installation

Add the following dependency to your Cargo.toml file:

[dependencies]
serde_yml = "0.0.7"

Usage

Serde YML offers a straightforward and intuitive API for working with YAML data in Rust. Here's a quick example of how to serialize and deserialize a Rust type:

use serde::{Serialize, Deserialize};

#[derive(Serialize, Deserialize)]
struct Point {
    x: f64,
    y: f64,
}

fn main() -> Result<(), serde_yml::Error> {
    let point = Point { x: 1.0, y: 2.0 };

    // Serialize to YAML
    let yaml = serde_yml::to_string(&point)?;
    assert_eq!(yaml, "x: 1.0\ny: 2.0\n");

    // Deserialize from YAML
    let deserialized_point: Point = serde_yml::from_str(&yaml)?;
    assert_eq!(point, deserialized_point);

    Ok(())
}

Examples

Serde YML provides a set of comprehensive examples to demonstrate its usage and capabilities. You can find them in the examples directory of the project.

To run the examples, clone the repository and execute the following command in your terminal from the project root directory:

cargo run --example example

The examples cover various scenarios, including serializing and deserializing structs, enums, optional fields, custom structs, and more.

Here are a few notable examples:

Serializing and Deserializing Structs

use serde::{Serialize, Deserialize};
use serde_yml;

#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct Point {
    x: f64,
    y: f64,
}

fn main() -> Result<(), serde_yml::Error> {
    let point = Point { x: 1.0, y: 2.0 };

    // Serialize to YAML
    let yaml = serde_yml::to_string(&point)?;
    assert_eq!(yaml, "x: 1.0\ny: 2.0\n");

    // Deserialize from YAML
    let deserialized_point: Point = serde_yml::from_str(&yaml)?;
    assert_eq!(point, deserialized_point);

    Ok(())
}

This example demonstrates how to serialize and deserialize a simple struct Point to and from YAML using the serde_yml crate.

divider

Serializing and Deserializing Enums

use serde::{Serialize, Deserialize};
use serde_yml;

#[derive(Serialize, Deserialize, PartialEq, Debug)]
enum Shape {
    Rectangle { width: u32, height: u32 },
    Circle { radius: f64 },
    Triangle { base: u32, height: u32 },
}

fn main() -> Result<(), serde_yml::Error> {
    let shapes = vec![
        Shape::Rectangle { width: 10, height: 20 },
        Shape::Circle { radius: 5.0 },
        Shape::Triangle { base: 8, height: 12 },
    ];

    // Serialize to YAML
    let yaml = serde_yml::to_string(&shapes)?;
    println!("Serialized YAML:\n{}", yaml);

    // Deserialize from YAML
    let deserialized_shapes: Vec<Shape> = serde_yml::from_str(&yaml)?;
    assert_eq!(shapes, deserialized_shapes);

    Ok(())
}

This example demonstrates how to serialize and deserialize an enum Shape (with struct variants) to and from YAML using the serde_yml crate.

divider

Serializing and Deserializing Optional Fields

use serde::{Serialize, Deserialize};
use serde_yml;

#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct User {
    name: String,
    age: Option<u32>,
    #[serde(default)]
    is_active: bool,
}

fn main() -> Result<(), serde_yml::Error> {
    let user = User {
        name: "John".to_string(),
        age: Some(30),
        is_active: true,
    };

    // Serialize to YAML
    let yaml = serde_yml::to_string(&user)?;
    println!("Serialized YAML:\n{}", yaml);

    // Deserialize from YAML
    let deserialized_user: User = serde_yml::from_str(&yaml)?;
    assert_eq!(user, deserialized_user);

    Ok(())
}

This example demonstrates how to serialize and deserialize a struct User with an optional field age to and from YAML using the serde_yml crate.

divider

Serializing and Deserializing a HashMap

use std::collections::HashMap;
use serde_yml;

fn main() -> Result<(), serde_yml::Error> {
    let mut map = HashMap::new();
    map.insert("name".to_string(), "John".to_string());
    map.insert("age".to_string(), "30".to_string());

    let yaml = serde_yml::to_string(&map)?;
    println!("Serialized YAML: {}", yaml);

    let deserialized_map: HashMap<String, serde_yml::Value> = serde_yml::from_str(&yaml)?;
    println!("Deserialized map: {:?}", deserialized_map);

    Ok(())
}

This example demonstrates how to serialize and deserialize a HashMap to and from YAML using the serde_yml crate.

divider

Serializing and Deserializing Custom Structs

use serde::{Serialize, Deserialize};
use serde_yml;

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

fn main() -> Result<(), serde_yml::Error> {
    let person = Person {
        name: "Alice".to_string(),
        age: 25,
        city: "New York".to_string(),
    };

    let yaml = serde_yml::to_string(&person)?;
    println!("Serialized YAML: {}", yaml);

    let deserialized_person: Person = serde_yml::from_str(&yaml)?;
    println!("Deserialized person: {:?}", deserialized_person);

    Ok(())
}

This example demonstrates how to serialize and deserialize a custom struct Person to and from YAML using the serde_yml crate.

divider

Using Serde derive

It can also be used with Serde's derive macros to handle structs and enums defined in your program.

Structs serialize in the obvious way:

use serde_derive::{Serialize, Deserialize};
use serde_yml;

#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct Point {
    x: f64,
    y: f64,
}

fn main() -> Result<(), serde_yml::Error> {
    let point = Point { x: 1.0, y: 2.0 };

    let yaml = serde_yml::to_string(&point)?;
    assert_eq!(yaml, "x: 1.0\n'y': 2.0\n");

    let deserialized_point: Point = serde_yml::from_str(&yaml)?;
    assert_eq!(point, deserialized_point);
    Ok(())
}

Enums serialize using YAML's !tag syntax to identify the variant name.

use serde_derive::{Serialize, Deserialize};
use serde_yml;

#[derive(Serialize, Deserialize, PartialEq, Debug)]
enum Enum {
    Unit,
    Newtype(usize),
    Tuple(usize, usize, usize),
    Struct { x: f64, y: f64 },
}

fn main() -> Result<(), serde_yml::Error> {
    let yaml = "
        - !Newtype 1
        - !Tuple [0, 0, 0]
        - !Struct {x: 1.0, y: 2.0}
    ";
    let values: Vec<Enum> = serde_yml::from_str(yaml).unwrap();
    assert_eq!(values[0], Enum::Newtype(1));
    assert_eq!(values[1], Enum::Tuple(0, 0, 0));
    assert_eq!(values[2], Enum::Struct { x: 1.0, y: 2.0 });

    // The last two in YAML's block style instead:
    let yaml = "
        - !Tuple
        - 0
        - 0
        - 0
        - !Struct
        x: 1.0
        'y': 2.0
    ";
    let values: Vec<Enum> = serde_yml::from_str(yaml).unwrap();
    assert_eq!(values[0], Enum::Tuple(0, 0, 0));
    assert_eq!(values[1], Enum::Struct { x: 1.0, y: 2.0 });

    // Variants with no data can be written using !Tag or just the string name.
    let yaml = "
        - Unit  # serialization produces this one
        - !Unit
    ";
    let values: Vec<Enum> = serde_yml::from_str(yaml).unwrap();
    assert_eq!(values[0], Enum::Unit);
    assert_eq!(values[1], Enum::Unit);

    Ok(())
}

This example demonstrates how to use Serde's derive macros to automatically implement the Serialize and Deserialize traits for a struct Point, and then serialize and deserialize it to and from YAML using the serde_yml crate.

divider

Serializing and Deserializing Enums with Custom Serialization and Deserialization

use serde::{Deserialize, Serialize};
use serde::de::{self, Deserializer, MapAccess, Visitor};
use serde::ser::{SerializeMap, Serializer};
use std::fmt;
use serde_yml;

#[derive(Serialize, Deserialize, PartialEq, Debug)]
enum MyEnum {
    Variant1(String),
    Variant2 { field: i32 },
}

#[derive(PartialEq, Debug)]
struct MyStruct {
    field: MyEnum,
}

// Include custom Serialize and Deserialize implementations for MyStruct here
// ...

fn main() -> Result<(), serde_yml::Error> {
    let input = MyStruct {
        field: MyEnum::Variant2 { field: 42 },
    };

    let yaml = serde_yml::to_string(&input).unwrap();
    println!("\n✅ Serialized YAML:\n{}", yaml);

    let output: MyStruct = serde_yml::from_str(&yaml).unwrap();
    println!("\n✅ Deserialized YAML:\n{:#?}", output);

    assert_eq!(input, output);

    Ok(())
}

This example demonstrates how to use custom Serialize and Deserialize implementations for a struct containing an enum field, and how to leverage serde_yml to serialize and deserialize the struct to and from YAML.

divider

Serializing and Deserializing Optional Enums

use serde::{Deserialize, Serialize};
use serde_yml;
use serde_yml::with::singleton_map_optional;

#[derive(Serialize, Deserialize, PartialEq, Debug)]
enum OptionalEnum {
    Variant1(String),
    Variant2 { field: i32 },
}

#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct OptionalStruct {
    #[serde(with = "singleton_map_optional")]
    field: Option<OptionalEnum>,
}

fn main() -> Result<(), serde_yml::Error> {
    let input = OptionalStruct {
        field: Some(OptionalEnum::Variant2 { field: 42 }),
    };

    let yaml = serde_yml::to_string(&input).unwrap();
    println!("\n✅ Serialized YAML:\n{}", yaml);

    let output: OptionalStruct = serde_yml::from_str(&yaml).unwrap();
    println!("\n✅ Deserialized YAML:\n{:#?}", output);

    assert_eq!(input, output);

    Ok(())
}

This example demonstrates how to use the singleton_map_optional attribute to serialize and deserialize an Option<Enum> field as a single YAML mapping entry with the key being the enum variant name.

divider

Serializing and Deserializing Nested Enums

use serde::{Deserialize, Serialize};
use serde_yml;
use serde_yml::with::singleton_map_recursive;

#[derive(Serialize, Deserialize, PartialEq, Debug)]
enum NestedEnum {
    Variant1(String),
    Variant2(Option<InnerEnum>),
}

#[derive(Serialize, Deserialize, PartialEq, Debug)]
enum InnerEnum {
    Inner1(i32),
    Inner2(i32),
}

#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct NestedStruct {
    #[serde(with = "singleton_map_recursive")]
    field: NestedEnum,
}

fn main() -> Result<(), serde_yml::Error> {
    let input = NestedStruct {
        field: NestedEnum::Variant2(Some(InnerEnum::Inner2(42))),
    };

    let yaml = serde_yml::to_string(&input).unwrap();
    println!("\n✅ Serialized YAML:\n{}", yaml);

    let output: NestedStruct = serde_yml::from_str(&yaml).unwrap();
    println!("\n✅ Deserialized YAML:\n{:#?}", output);

    assert_eq!(input, output);

    Ok(())
}

This example demonstrates how to use the singleton_map_recursive attribute to serialize and deserialize a nested enum structure where one of the enum variants contains an optional inner enum.

divider

Serializing and Deserializing Enums with singleton_map_recursive

use serde::{Deserialize, Serialize};
use serde_yml;
use serde_yml::with::singleton_map_recursive;

#[derive(Serialize, Deserialize, PartialEq, Debug)]
enum MyEnum {
    Variant1(String),
    Variant2 { field: i32 },
}

#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct MyStruct {
    #[serde(with = "singleton_map_recursive")]
    field: MyEnum,
}

fn main() -> Result<(), serde_yml::Error> {
    let input = MyStruct {
        field: MyEnum::Variant2 { field: 42 },
    };

    let yaml = serde_yml::to_string(&input).unwrap();
    println!("\n✅ Serialized YAML:\n{}", yaml);

    let output: MyStruct = serde_yml::from_str(&yaml).unwrap();
    println!("\n✅ Deserialized YAML:\n{:#?}", output);

    assert_eq!(input, output);

    Ok(())
}

This example demonstrates how to use the singleton_map_recursive attribute to serialize and deserialize an enum field as a single YAML mapping entry with the key being the enum variant name.

divider

Serializing and Deserializing Enums with singleton_map_with and Custom Serialization

use serde::{Deserialize, Serialize};
use serde_yml;
use serde_yml::with::singleton_map_with;

fn custom_serialize<T, S>(
    value: &T,
    serializer: S,
) -> Result<S::Ok, S::Error>
where
    T: Serialize,
    S: serde::Serializer,
{
    // Custom serialization logic
    singleton_map_with::serialize(value, serializer)
}

#[derive(Serialize, Deserialize, PartialEq, Debug)]
enum MyEnum {
    Variant1(String),
    Variant2 { field: i32 },
}

#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct MyStruct {
    #[serde(
        serialize_with = "custom_serialize",
        deserialize_with = "singleton_map_with::deserialize"
    )]
    field: MyEnum,
}

fn main() -> Result<(), serde_yml::Error> {
    let input = MyStruct {
        field: MyEnum::Variant2 { field: 42 },
    };
    let yaml = serde_yml::to_string(&input).unwrap();
    println!("\n✅ Serialized YAML:\n{}", yaml);

    let output: MyStruct = serde_yml::from_str(&yaml).unwrap();
    println!("\n✅ Deserialized YAML:\n{:#?}", output);
    assert_eq!(input, output);

    Ok(())
}

This example demonstrates how to use the singleton_map_with attribute in combination with a custom serialization function (custom_serialize) to serialize and deserialize an enum field (MyEnum) within a struct (MyStruct).

The custom_serialize function is used for serialization, while the singleton_map_with::deserialize function is used for deserialization. This allows for additional customization of the serialization process while still leveraging the singleton_map_with attribute for deserialization.

divider

Serializing and Deserializing Enums with singleton_map_with

use serde::{Deserialize, Serialize};
use serde_yml;
use serde_yml::with::singleton_map_with;

#[derive(Serialize, Deserialize, PartialEq, Debug)]
enum MyEnum {
    Variant1(String),
    Variant2 { field: i32 },
}

#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct MyStruct {
    #[serde(with = "singleton_map_with")]
    field: MyEnum,
}

fn main() -> Result<(), serde_yml::Error> {
    let input = MyStruct {
        field: MyEnum::Variant2 { field: 42 },
    };
    let yaml = serde_yml::to_string(&input).unwrap();
    println!("\n✅ Serialized YAML:\n{}", yaml);

    let output: MyStruct = serde_yml::from_str(&yaml).unwrap();
    println!("\n✅ Deserialized YAML:\n{:#?}", output);
    assert_eq!(input, output);

    Ok(())
}

This example demonstrates how to use the singleton_map_with attribute to serialize and deserialize an enum field (MyEnum) within a struct (MyStruct). The singleton_map_with attribute allows for additional customization of the serialization and deserialization process through the use of helper functions.

divider

Semantic Versioning Policy

For transparency into our release cycle and in striving to maintain backward compatibility, serde_yml follows semantic versioning.

License

The project is licensed under the terms of both the MIT license and the Apache License (Version 2.0).

Contribution

We welcome all people who want to contribute. Please see the contributing instructions for more information.

Contributions in any form (issues, pull requests, etc.) to this project must adhere to the Rust's Code of Conduct.

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

Acknowledgements

A big thank you to all the awesome contributors of serde_yml for their help and support. A special thank you goes to David Tolnay and his work on Serde YAML for inspiring this project.

Dependencies

~2.2–3.5MB
~67K SLoC