third_party_rust_serde/README.md

736 lines
22 KiB
Markdown
Raw Normal View History

2015-04-01 05:16:15 +00:00
Serde Rust Serialization Framework
==================================
2014-09-07 08:30:58 +00:00
[![Build Status](https://api.travis-ci.org/serde-rs/serde.svg?branch=master)](https://travis-ci.org/serde-rs/serde)
2015-08-30 04:09:12 +00:00
[![Coverage Status](https://coveralls.io/repos/serde-rs/serde/badge.svg?branch=master&service=github)](https://coveralls.io/github/serde-rs/serde?branch=master)
[![Latest Version](https://img.shields.io/crates/v/serde.svg)](https://crates.io/crates/serde)
2014-09-07 08:30:58 +00:00
2015-04-01 05:16:15 +00:00
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
2015-04-02 04:59:37 +00:00
serializees can be completely optimized away, leaving Serde to perform roughly
2015-04-01 05:16:15 +00:00
the same speed as a hand written serializer for a specific type.
2015-08-09 23:37:01 +00:00
Documentation is available at:
2016-03-07 02:06:11 +00:00
* [serde](https://serde-rs.github.io/serde/serde/index.html)
2015-08-09 23:37:01 +00:00
2015-09-07 20:10:23 +00:00
Using Serde with Nightly Rust and serde\_macros
===============================================
2015-08-09 23:37:01 +00:00
Here is a simple example that demonstrates how to use Serde by serializing and
deserializing to JSON. Serde comes with some powerful code generation libraries
that work with Stable and Nightly Rust that eliminate much of the complexity of
hand rolling serialization and deserialization for a given type. First lets see
how we would use Nightly Rust, which is currently a bit simpler than Stable
Rust:
`Cargo.toml`:
```toml
[package]
name = "serde_example_nightly"
version = "0.1.0"
authors = ["Erick Tryzelaar <erick.tryzelaar@gmail.com>"]
[dependencies]
serde = "*"
serde_json = "*"
serde_macros = "*"
```
2015-04-01 05:16:15 +00:00
2015-08-09 23:37:01 +00:00
`src/main.rs`
```rust
2015-06-06 12:38:33 +00:00
#![feature(custom_derive, plugin)]
#![plugin(serde_macros)]
2015-04-02 04:59:37 +00:00
extern crate serde;
2015-08-09 23:37:01 +00:00
extern crate serde_json;
#[derive(Serialize, Deserialize, Debug)]
struct Point {
x: i32,
y: i32,
}
fn main() {
let point = Point { x: 1, y: 2 };
let serialized = serde_json::to_string(&point).unwrap();
println!("{}", serialized);
let deserialized: Point = serde_json::from_str(&serialized).unwrap();
2015-08-09 23:37:01 +00:00
println!("{:?}", deserialized);
}
```
When run, it produces:
```
% cargo run
{"x":1,"y":2}
Point { x: 1, y: 2 }
```
2015-09-07 20:10:23 +00:00
Using Serde with Stable Rust, syntex, and serde\_codegen
========================================================
2015-08-09 23:37:01 +00:00
Stable Rust is a little more complicated because it does not yet support
compiler plugins. Instead we need to use the code generation library
[syntex](https://github.com/serde-rs/syntex) for this:
2015-08-09 23:37:01 +00:00
```toml
[package]
name = "serde_example"
version = "0.1.0"
authors = ["Erick Tryzelaar <erick.tryzelaar@gmail.com>"]
build = "build.rs"
[build-dependencies]
serde_codegen = "*"
syntex = "*"
[dependencies]
serde = "*"
serde_json = "*"
```
`src/main.rs`:
```rust,ignore
2015-08-09 23:37:01 +00:00
extern crate serde;
extern crate serde_json;
2015-04-02 04:59:37 +00:00
2015-08-09 23:37:01 +00:00
include!(concat!(env!("OUT_DIR"), "/main.rs"));
```
`src/main.rs.in`:
2015-04-02 04:59:37 +00:00
```rust,ignore
2015-08-09 23:37:01 +00:00
#[derive(Serialize, Deserialize, Debug)]
2015-04-01 05:16:15 +00:00
struct Point {
x: i32,
y: i32,
}
2015-08-09 23:37:01 +00:00
fn main() {
let point = Point { x: 1, y: 2 };
let serialized = serde_json::to_string(&point).unwrap();
println!("{}", serialized);
let deserialized: Point = serde_json::from_str(&serialized).unwrap();
2015-08-09 23:37:01 +00:00
println!("{:?}", deserialized);
}
2015-04-02 04:59:37 +00:00
```
2015-04-01 05:16:15 +00:00
`build.rs`
```rust,ignore
extern crate syntex;
extern crate serde_codegen;
use std::env;
use std::path::Path;
pub fn main() {
let out_dir = env::var_os("OUT_DIR").unwrap();
let src = Path::new("src/main.rs.in");
let dst = Path::new(&out_dir).join("main.rs");
let mut registry = syntex::Registry::new();
serde_codegen::register(&mut registry);
registry.expand("", &src, &dst).unwrap();
}
```
2015-08-09 23:37:01 +00:00
This also produces:
```
% cargo run
{"x":1,"y":2}
Point { x: 1, y: 2 }
```
While this works well with Stable Rust, be aware that the error locations
currently are reported in the generated file instead of in the source file. You
may find it easier to develop with Nightly Rust and `serde\_macros`, then
deploy with Stable Rust and `serde_codegen`. It's possible to combine both
approaches in one setup:
`Cargo.toml`:
```toml
[package]
name = "serde_example"
version = "0.1.0"
authors = ["Erick Tryzelaar <erick.tryzelaar@gmail.com>"]
build = "build.rs"
[features]
default = ["serde_codegen"]
nightly = ["serde_macros"]
[build-dependencies]
serde_codegen = { version = "*", optional = true }
syntex = "*"
[dependencies]
serde = "*"
serde_json = "*"
serde_macros = { version = "*", optional = true }
```
`build.rs`:
2015-04-02 04:59:37 +00:00
```rust,ignore
2015-08-09 23:37:01 +00:00
#[cfg(not(feature = "serde_macros"))]
mod inner {
extern crate syntex;
extern crate serde_codegen;
2015-04-02 04:59:37 +00:00
2015-08-09 23:37:01 +00:00
use std::env;
use std::path::Path;
2015-04-02 04:59:37 +00:00
2015-08-09 23:37:01 +00:00
pub fn main() {
let out_dir = env::var_os("OUT_DIR").unwrap();
2015-04-02 04:59:37 +00:00
2015-08-09 23:37:01 +00:00
let src = Path::new("src/main.rs.in");
let dst = Path::new(&out_dir).join("main.rs");
2015-04-02 04:59:37 +00:00
2015-08-09 23:37:01 +00:00
let mut registry = syntex::Registry::new();
serde_codegen::register(&mut registry);
registry.expand("", &src, &dst).unwrap();
}
}
#[cfg(feature = "serde_macros")]
mod inner {
pub fn main() {}
}
fn main() {
inner::main();
}
2015-04-02 04:59:37 +00:00
```
2015-08-09 23:37:01 +00:00
`src/main.rs`:
2015-04-02 04:59:37 +00:00
```rust,ignore
2015-08-09 23:37:01 +00:00
#![cfg_attr(feature = "serde_macros", feature(custom_derive, plugin))]
#![cfg_attr(feature = "serde_macros", plugin(serde_macros))]
2015-04-02 04:59:37 +00:00
2015-08-09 23:37:01 +00:00
extern crate serde;
extern crate serde_json;
#[cfg(feature = "serde_macros")]
include!("main.rs.in");
2015-04-02 04:59:37 +00:00
2015-08-09 23:37:01 +00:00
#[cfg(not(feature = "serde_macros"))]
include!(concat!(env!("OUT_DIR"), "/main.rs"));
2015-04-02 04:59:37 +00:00
```
2015-08-09 23:37:01 +00:00
The `src/main.rs.in` is the same as before.
2015-09-07 20:10:23 +00:00
Then to run with stable:
```
% cargo build
...
```
Or with nightly:
```
2015-09-07 20:10:23 +00:00
% cargo build --features nightly --no-default-features
...
```
2015-09-07 20:10:23 +00:00
2015-04-02 04:59:37 +00:00
Serialization without Macros
============================
Under the covers, Serde extensively uses the Visitor pattern to thread state
between the
[Serializer](http://serde-rs.github.io/serde/serde/serde/ser/trait.Serializer.html)
2015-04-02 04:59:37 +00:00
and
[Serialize](http://serde-rs.github.io/serde/serde/serde/ser/trait.Serialize.html)
2015-04-02 04:59:37 +00:00
without the two having specific information about each other's concrete type.
This has many of the same benefits as frameworks that use runtime type
information without the overhead. In fact, when compiling with optimizations,
Rust is able to remove most or all the visitor state, and generate code that's
nearly as fast as a hand written serializer format for a specific type.
To see it in action, lets look at how a simple type like `i32` is serialized.
The
[Serializer](http://serde-rs.github.io/serde/serde/serde/ser/trait.Serializer.html)
2015-04-02 04:59:37 +00:00
is threaded through the type:
```rust,ignore
2015-04-02 04:59:37 +00:00
impl serde::Serialize for i32 {
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
where S: serde::Serializer,
{
serializer.serialize_i32(*self)
2015-04-02 04:59:37 +00:00
}
}
```
As you can see it's pretty simple. More complex types like `BTreeMap` need to
pass a
[MapVisitor](http://serde-rs.github.io/serde/serde/serde/ser/trait.MapVisitor.html)
2015-04-02 04:59:37 +00:00
to the
[Serializer](http://serde-rs.github.io/serde/serde/serde/ser/trait.Serializer.html)
2015-04-02 04:59:37 +00:00
in order to walk through the type:
```rust,ignore
2015-04-02 04:59:37 +00:00
impl<K, V> Serialize for BTreeMap<K, V>
where K: Serialize + Ord,
V: Serialize,
{
#[inline]
2015-04-01 05:16:15 +00:00
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
2015-04-02 04:59:37 +00:00
where S: Serializer,
2015-04-01 05:16:15 +00:00
{
serializer.serialize_map(MapIteratorVisitor::new(self.iter(), Some(self.len())))
2015-04-02 04:59:37 +00:00
}
}
pub struct MapIteratorVisitor<Iter> {
iter: Iter,
len: Option<usize>,
}
impl<K, V, Iter> MapIteratorVisitor<Iter>
where Iter: Iterator<Item=(K, V)>
{
#[inline]
pub fn new(iter: Iter, len: Option<usize>) -> MapIteratorVisitor<Iter> {
MapIteratorVisitor {
iter: iter,
len: len,
2015-04-01 05:16:15 +00:00
}
2015-04-02 04:59:37 +00:00
}
}
2015-04-01 05:16:15 +00:00
2015-04-02 04:59:37 +00:00
impl<K, V, I> MapVisitor for MapIteratorVisitor<I>
where K: Serialize,
V: Serialize,
I: Iterator<Item=(K, V)>,
{
#[inline]
fn visit<S>(&mut self, serializer: &mut S) -> Result<Option<()>, S::Error>
where S: Serializer,
{
match self.iter.next() {
Some((key, value)) => {
let value = try!(serializer.serialize_map_elt(key, value));
2015-04-02 04:59:37 +00:00
Ok(Some(value))
2015-04-01 05:16:15 +00:00
}
2015-04-02 04:59:37 +00:00
None => Ok(None)
2015-04-01 05:16:15 +00:00
}
2015-04-02 04:59:37 +00:00
}
#[inline]
fn len(&self) -> Option<usize> {
self.len
}
}
```
Serializing structs follow this same pattern. In fact, structs are represented
2015-06-29 14:18:48 +00:00
as a named map. Its visitor uses a simple state machine to iterate through all
2015-04-02 04:59:37 +00:00
the fields:
```rust
2015-09-07 20:10:23 +00:00
extern crate serde;
extern crate serde_json;
2015-09-07 20:10:23 +00:00
2015-04-02 04:59:37 +00:00
struct Point {
x: i32,
y: i32,
}
2015-04-01 05:16:15 +00:00
2015-04-02 04:59:37 +00:00
impl serde::Serialize for Point {
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
where S: serde::Serializer
{
serializer.serialize_struct("Point", PointMapVisitor {
2015-04-01 05:16:15 +00:00
value: self,
state: 0,
})
}
}
2015-04-02 04:59:37 +00:00
struct PointMapVisitor<'a> {
value: &'a Point,
state: u8,
}
impl<'a> serde::ser::MapVisitor for PointMapVisitor<'a> {
fn visit<S>(&mut self, serializer: &mut S) -> Result<Option<()>, S::Error>
2015-04-02 04:59:37 +00:00
where S: serde::Serializer
{
match self.state {
0 => {
self.state += 1;
Ok(Some(try!(serializer.serialize_struct_elt("x", &self.value.x))))
2015-04-02 04:59:37 +00:00
}
1 => {
self.state += 1;
Ok(Some(try!(serializer.serialize_struct_elt("y", &self.value.y))))
2015-04-02 04:59:37 +00:00
}
_ => {
Ok(None)
}
}
}
}
fn main() {
let point = Point { x: 1, y: 2 };
let serialized = serde_json::to_string(&point).unwrap();
println!("{}", serialized);
}
```
2015-04-02 04:59:37 +00:00
Deserialization without Macros
==============================
Deserialization is a little more complicated since there's a bit more error
handling that needs to occur. Let's start with the simple `i32`
[Deserialize](http://serde-rs.github.io/serde/serde/serde/de/trait.Deserialize.html)
2015-04-02 04:59:37 +00:00
implementation. It passes a
[Visitor](http://serde-rs.github.io/serde/serde/serde/de/trait.Visitor.html) to the
[Deserializer](http://serde-rs.github.io/serde/serde/serde/de/trait.Deserializer.html).
The [Visitor](http://serde-rs.github.io/serde/serde/serde/de/trait.Visitor.html)
2015-04-02 04:59:37 +00:00
can create the `i32` from a variety of different types:
```rust,ignore
2015-04-02 04:59:37 +00:00
impl Deserialize for i32 {
2015-04-13 11:37:06 +00:00
fn deserialize<D>(deserializer: &mut D) -> Result<i32, D::Error>
2015-04-02 04:59:37 +00:00
where D: serde::Deserializer,
{
deserializer.deserialize(I32Visitor)
2015-04-02 04:59:37 +00:00
}
}
struct I32Visitor;
impl serde::de::Visitor for I32Visitor {
type Value = i32;
2016-01-17 14:14:25 +00:00
fn visit_i16<E>(&mut self, value: i16) -> Result<i32, E>
2015-04-02 04:59:37 +00:00
where E: Error,
{
self.visit_i32(value as i32)
}
fn visit_i32<E>(&mut self, value: i32) -> Result<i32, E>
where E: Error,
{
Ok(value)
}
...
```
Since it's possible for this type to get passed an unexpected type, we need a
way to error out. This is done by way of the
[Error](http://serde-rs.github.io/serde/serde/serde/de/trait.Error.html) trait,
2015-04-02 04:59:37 +00:00
which allows a
[Deserialize](http://serde-rs.github.io/serde/serde/serde/de/trait.Deserialize.html)
2015-04-02 04:59:37 +00:00
to generate an error for a few common error conditions. Here's how it could be used:
```rust,ignore
2015-04-02 04:59:37 +00:00
...
fn visit_string<E>(&mut self, _: String) -> Result<i32, E>
where E: Error,
{
Err(serde::de::Error::custom("expect a string"))
2015-04-02 04:59:37 +00:00
}
...
```
Maps follow a similar pattern as before, and use a
[MapVisitor](http://serde-rs.github.io/serde/serde/serde/de/trait.MapVisitor.html)
2015-04-02 04:59:37 +00:00
to walk through the values generated by the
[Deserializer](http://serde-rs.github.io/serde/serde/serde/de/trait.Deserializer.html).
2015-04-02 04:59:37 +00:00
```rust,ignore
2015-04-02 04:59:37 +00:00
impl<K, V> serde::Deserialize for BTreeMap<K, V>
where K: serde::Deserialize + Eq + Ord,
V: serde::Deserialize,
{
fn deserialize<D>(deserializer: &mut D) -> Result<BTreeMap<K, V>, D::Error>
where D: serde::Deserializer,
{
deserializer.deserialize(BTreeMapVisitor::new())
2015-04-02 04:59:37 +00:00
}
}
pub struct BTreeMapVisitor<K, V> {
marker: PhantomData<BTreeMap<K, V>>,
}
impl<K, V> BTreeMapVisitor<K, V> {
pub fn new() -> Self {
BTreeMapVisitor {
marker: PhantomData,
}
}
}
impl<K, V> serde::de::Visitor for BTreeMapVisitor<K, V>
where K: serde::de::Deserialize + Ord,
V: serde::de::Deserialize
{
type Value = BTreeMap<K, V>;
fn visit_unit<E>(&mut self) -> Result<BTreeMap<K, V>, E>
where E: Error,
{
Ok(BTreeMap::new())
}
fn visit_map<V_>(&mut self, mut visitor: V_) -> Result<BTreeMap<K, V>, V_::Error>
where V_: MapVisitor,
{
let mut values = BTreeMap::new();
while let Some((key, value)) = try!(visitor.visit()) {
values.insert(key, value);
}
try!(visitor.end());
Ok(values)
}
}
```
Deserializing structs goes a step further in order to support not allocating a
`String` to hold the field names. This is done by custom field enum that
deserializes an enum variant from a string. So for our `Point` example from
before, we need to generate:
2015-04-01 05:16:15 +00:00
```rust
2015-09-07 20:10:23 +00:00
extern crate serde;
extern crate serde_json;
2015-09-07 20:10:23 +00:00
#[derive(Debug)]
2015-09-07 20:10:23 +00:00
struct Point {
x: i32,
y: i32,
}
2015-04-01 05:16:15 +00:00
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 PointFieldVisitor;
2015-04-01 05:16:15 +00:00
impl serde::de::Visitor for PointFieldVisitor {
type Value = PointField;
2015-04-01 05:16:15 +00:00
fn visit_str<E>(&mut self, value: &str) -> Result<PointField, E>
where E: serde::de::Error
{
match value {
"x" => Ok(PointField::X),
"y" => Ok(PointField::Y),
_ => Err(serde::de::Error::custom("expected x or y")),
2015-04-01 05:16:15 +00:00
}
}
}
deserializer.deserialize(PointFieldVisitor)
2015-04-01 05:16:15 +00:00
}
}
2015-04-01 05:16:15 +00:00
impl serde::Deserialize for Point {
fn deserialize<D>(deserializer: &mut D) -> Result<Point, D::Error>
where D: serde::de::Deserializer
{
static FIELDS: &'static [&'static str] = &["x", "y"];
deserializer.deserialize_struct("Point", FIELDS, PointVisitor)
2015-04-02 04:59:37 +00:00
}
}
2015-04-01 05:16:15 +00:00
2015-04-02 04:59:37 +00:00
struct PointVisitor;
2015-04-01 05:16:15 +00:00
2015-04-02 04:59:37 +00:00
impl serde::de::Visitor for PointVisitor {
type Value = Point;
2015-04-01 05:16:15 +00:00
fn visit_map<V>(&mut self, mut visitor: V) -> Result<Point, V::Error>
2015-04-02 04:59:37 +00:00
where V: serde::de::MapVisitor
{
let mut x = None;
let mut y = None;
2015-04-01 05:16:15 +00:00
2015-04-02 04:59:37 +00:00
loop {
match try!(visitor.visit_key()) {
Some(PointField::X) => { x = Some(try!(visitor.visit_value())); }
Some(PointField::Y) => { y = Some(try!(visitor.visit_value())); }
2015-04-02 04:59:37 +00:00
None => { break; }
2015-04-01 05:16:15 +00:00
}
}
2015-04-02 04:59:37 +00:00
let x = match x {
Some(x) => x,
None => try!(visitor.missing_field("x")),
};
2015-04-01 05:16:15 +00:00
2015-04-02 04:59:37 +00:00
let y = match y {
Some(y) => y,
None => try!(visitor.missing_field("y")),
};
2015-04-01 05:16:15 +00:00
2015-04-02 04:59:37 +00:00
try!(visitor.end());
2015-04-01 05:16:15 +00:00
2015-04-02 04:59:37 +00:00
Ok(Point{ x: x, y: y })
}
2015-04-01 05:16:15 +00:00
}
fn main() {
let serialized = "{\"x\":1,\"y\":2}";
let deserialized: Point = serde_json::from_str(&serialized).unwrap();
println!("{:?}", deserialized);
}
2015-04-01 05:16:15 +00:00
```
Design Considerations and tradeoffs for Serializers and Deserializers
=====================================================================
Serde serialization and deserialization implementations are written in such a
way that they err on being able to represent more values, and also provide
better error messages when they are passed an incorrect type to deserialize
from. For example, by default, it is a syntax error to deserialize a `String`
into an `Option<String>`. This is implemented such that it is possible to
distinguish between the values `None` and `Some(())`, if the serialization
format supports option types.
However, many formats do not have option types, and represents optional values
as either a `null`, or some other value. Serde `Serializer`s and
`Deserializer`s can opt-in support for this. For serialization, this is pretty
easy. Simply implement these methods:
```rust,ignore
...
fn visit_none(&mut self) -> Result<(), Self::Error> {
self.visit_unit()
}
fn visit_some<T>(&mut self, value: T) -> Result<(), Self::Error> {
value.serialize(self)
}
...
```
For deserialization, this can be implemented by way of the
`Deserializer::visit_option` hook, which presumes that there is some ability to peek at what is the
next value in the serialized token stream. This following example is from
[serde_tests::TokenDeserializer](https://github.com/serde-rs/serde/blob/master/serde_tests/tests/token.rs#L435-L454),
where it checks to see if the next value is an `Option`, a `()`, or some other
value:
```rust,ignore
...
fn visit_option<V>(&mut self, mut visitor: V) -> Result<V::Value, Error>
where V: de::Visitor,
{
match self.tokens.peek() {
Some(&Token::Option(false)) => {
self.tokens.next();
visitor.visit_none()
}
Some(&Token::Option(true)) => {
self.tokens.next();
visitor.visit_some(self)
}
Some(&Token::Unit) => {
self.tokens.next();
visitor.visit_none()
}
Some(_) => visitor.visit_some(self),
None => Err(Error::EndOfStreamError),
}
}
...
```
2015-09-07 23:54:27 +00:00
Annotations
===========
`serde_codegen` and `serde_macros` support annotations that help to customize
how types are serialized. Here are the supported annotations:
Container Annotations:
| Annotation | Function |
| ---------- | -------- |
| `#[serde(rename="name")` | Serialize and deserialize this container with the given name |
| `#[serde(rename(serialize="name1"))` | Serialize this container with the given name |
| `#[serde(rename(deserialize="name1"))` | Deserialize this container with the given name |
| `#[serde(deny_unknown_fields)` | Always error during serialization when encountering unknown fields. When absent, unknown fields are ignored for self-describing formats like JSON. |
Variant Annotations:
2015-09-07 23:54:27 +00:00
| Annotation | Function |
| ---------- | -------- |
| `#[serde(rename="name")` | Serialize and deserialize this variant with the given name |
| `#[serde(rename(serialize="name1"))` | Serialize this variant with the given name |
| `#[serde(rename(deserialize="name1"))` | Deserialize this variant with the given name |
Field Annotations:
| Annotation | Function |
| ---------- | -------- |
| `#[serde(rename="name")` | Serialize and deserialize this field with the given name |
| `#[serde(rename(serialize="name1"))` | Serialize this field with the given name |
| `#[serde(rename(deserialize="name1"))` | Deserialize this field with the given name |
| `#[serde(default)` | If the value is not specified, use the `Default::default()` |
| `#[serde(default="$path")` | Call the path to a function `fn() -> T` to build the value |
| `#[serde(skip_serializing)` | Do not serialize this value |
| `#[serde(skip_serializing_if="$path")` | Do not serialize this value if this function `fn(&T) -> bool` returns `false` |
| `#[serde(serialize_with="$path")` | Call a function `fn<T, S>(&T, &mut S) -> Result<(), S::Error> where S: Serializer` to serialize this value |
| `#[serde(deserialize_with="$path")` | Call a function `fn<T, D>(&mut D) -> Result<T, D::Error> where D: Deserializer` to deserialize this value |
Upgrading from Serde 0.6
========================
* `#[serde(skip_serializing_if_none)]` was replaced with `#[serde(skip_serializing_if="Option::is_none")]`.
* `#[serde(skip_serializing_if_empty)]` was replaced with `#[serde(skip_serializing_if="Vec::is_empty")]`.
Serialization Formats Using Serde
=================================
2016-02-24 08:22:48 +00:00
| Format | Name |
| ------ | ---- |
| Bincode | [bincode](https://crates.io/crates/bincode) |
| JSON | [serde\_json](https://crates.io/crates/serde_json) |
| MessagePack | [rmp](https://crates.io/crates/rmp) |
| XML | [serde\_xml](https://github.com/serde-rs/xml) |
| YAML | [serde\_yaml](https://github.com/dtolnay/serde-yaml) |