Skip to content
/ serde Public

Serialization framework for Rust

License

Apache-2.0, MIT licenses found

Licenses found

Apache-2.0
LICENSE-APACHE
MIT
LICENSE-MIT
Notifications You must be signed in to change notification settings

serde-rs/serde

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Serde Rust Serialization Framework

Build Status

Serde is a powerful framework that enables serialization libraries to generically serialize Rust data structures without the overhead of runtime type information. In many situations, the handshake protocol between serializers and serializees can be completely optimized away, leaving serde to perform roughly the same speed as a hand written serializer for a specific type.

Documentation is available at http:https://erickt.github.io/rust-serde/serde

Example

Serde works by threading visitors between the serializer and the serializee. This allows data to be generically shared between the two without needing to wrap the values in a separate structure. Here's an example struct serializer. It works by reinterpreting the the structure as a named map, with the keys being the stringified field name, and a simple state machine to step through each field:

struct Point {
    x: i32,
    y: i32,
}

impl serde::Serialize for Point {
    fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
        where S: serialize::Serializer
    {
        struct MapVisitor<'a> {
            value: &'a Point,
            state: u8,
        }

        impl<'a> serde::ser::MapVisitor for MapVisitor {
            fn visit<S>(&mut self, serializer: &mut S) -> Result<Option(), S::Error> {
                match self.state {
                    0 => {
                        self.state += 1;
                        Ok(Some(try!(serializer.visit_map_elt("x", &self.x)))
                    }
                    1 => {
                        self.state += 1;
                        Ok(Some(try!(serializer.visit_map_elt("y", &self.y))))
                    }
                    _ => {
                        Ok(None)
                    }
                }
            }
        }

        serializer.visit_named_map("Point", MapVisitor {
            value: self,
            state: 0,
        })
    }
}

Deserialization is a bit more tricky. We need to deserialize a field from a string, but in order to avoid some borrow checker issues and in desire to avoid allocations, we deserialize field names into an enum:

enum PointField {
    X,
    Y,
}

impl serde::Deserialize for PointField {
    fn deserialize<D>(deserializer: &mut D) -> Result<PointField, D::Error>
        where D: serde::de::Deserializer
    {
        struct FieldVisitor;

        impl serde::de::Visitor for FieldVisitor {
            type Value = Field;

            fn visit_str<E>(&mut self, value: &str) -> Result<PointField, E>
                where E: serde::de::Error
            {
                match value {
                    "x" => Ok(Field::X),
                    "y" => Ok(Field::Y),
                    _ => Err(serde::de::Error::syntax_error()),
                }
            }
        }

        deserializer.visit(FieldVisitor)
    }
}

This is then used in our actual deserializer:

impl serde::Deserialize for Point {
    fn deserialize<D>(deserializer: &mut D) -> Result<Point, D::Error>
        where D: serde::de::Deserializer
    {
        struct PointVisitor;

        impl serde::de::Visitor for PointVisitor {
            type Value = Point;

            fn visit_map<V>(&mut self, mut visitor: V) -> Result<Point, V::Error>
                where V: serde::de::MapVisitor
            {
                let mut x = None;
                let mut y = None;

                loop {
                    match try!(visitor.visit_key()) {
                        Some(Field::X) => { x = Some(try!(visitor.visit_value())); }
                        Some(Field::Y) => { y = Some(try!(visitor.visit_value())); }
                        None => { break; }
                    }
                }

                let x = match x {
                    Some(x) => x,
                    None => try!(visitor.missing_field("x")),
                };

                let y = match y {
                    Some(y) => y,
                    None => try!(visitor.missing_field("y")),
                };

                try!(visitor.end());

                Ok(Point{ x: x, y: y })
            }
        }

        deserializer.visit_named_map("Point", PointVisitor)
    }
}

There's a bit of machinery required to write implementations of Serialize and Deserialize. Fortunately it is not necessary in most circumstances. Instead, it's much easier to use the serde_macros plugin. The prior code can be rewritten as:

#![feature(custom_derive)]
#![plugin(serde_macros)]

extern crate serde;

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