Repository: tafia/quick-xml
Pull request #173 (2019-12-10 07:40:11)

add serialize
Add Serialize

For the moment it matches serde-xml-rs features (no seq etc ...).
It also escapes text when needed.

The missing features shouldn't be too hard to add later on.


Patch:
diff --git a/README.md b/README.md
index 4efac59..9e21e8b 100644
--- a/README.md
+++ b/README.md
@@ -105,6 +105,115 @@ let expected = r#"<my_elem k1="v1" k2="v2" my-key="some value"><child>text</chil
 assert_eq!(result, expected.as_bytes());
 ```
 
+## Serde
+
+When using the `serialize` feature, quick-xml can be used with serde's `Serialize`/`Deserialize` traits.
+
+This has largely been inspired by [serde-xml-rs](https://github.com/RReverser/serde-xml-rs). 
+quick-xml follows its convention for deserialization, including the 
+[`$value`](https://github.com/RReverser/serde-xml-rs#parsing-the-value-of-a-tag) special name.
+
+Note that despite not focusing on performance (there are several unecessary copies), it remains about 10x faster than serde-xml-rs.
+
+Here is an example deserializing crates.io source:
+
+```rust
+// Cargo.toml
+// [dependencies]
+// serde = { version = "1.0", features = [ "derive" ] }
+// quick_xml = { version = "0.17", features = [ "serialize" ] }
+extern crate serde;
+extern crate quick_xml;
+
+use serde::Deserialize;
+use quick_xml::de::{from_str, DeError};
+
+#[derive(Debug, Deserialize, PartialEq)]
+struct Link {
+    rel: String,
+    href: String,
+    sizes: Option<String>,
+}
+
+#[derive(Debug, Deserialize, PartialEq)]
+#[serde(rename_all = "lowercase")]
+enum Lang {
+    En,
+    Fr,
+    De,
+}
+
+#[derive(Debug, Deserialize, PartialEq)]
+struct Head {
+    title: String,
+    #[serde(rename = "link", default)]
+    links: Vec<Link>,
+}
+
+#[derive(Debug, Deserialize, PartialEq)]
+struct Script {
+    src: String,
+    integrity: String,
+}
+
+#[derive(Debug, Deserialize, PartialEq)]
+struct Body {
+    #[serde(rename = "script", default)]
+    scripts: Vec<Script>,
+}
+
+#[derive(Debug, Deserialize, PartialEq)]
+struct Html {
+    lang: Option<String>,
+    head: Head,
+    body: Body,
+}
+
+fn crates_io() -> Result<Html, DeError> {
+    let xml = "<!DOCTYPE html>
+        <html lang=\"en\">
+          <head>
+            <meta charset=\"utf-8\">
+            <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">
+            <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">
+
+            <title>crates.io: Rust Package Registry</title>
+
+
+        <!-- EMBER_CLI_FASTBOOT_TITLE --><!-- EMBER_CLI_FASTBOOT_HEAD -->
+        <link rel=\"manifest\" href=\"/manifest.webmanifest\">
+        <link rel=\"apple-touch-icon\" href=\"/cargo-835dd6a18132048a52ac569f2615b59d.png\" sizes=\"227x227\">
+
+            <link rel=\"stylesheet\" href=\"/assets/vendor-8d023d47762d5431764f589a6012123e.css\" integrity=\"sha256-EoB7fsYkdS7BZba47+C/9D7yxwPZojsE4pO7RIuUXdE= sha512-/SzGQGR0yj5AG6YPehZB3b6MjpnuNCTOGREQTStETobVRrpYPZKneJwcL/14B8ufcvobJGFDvnTKdcDDxbh6/A==\" >
+            <link rel=\"stylesheet\" href=\"/assets/cargo-cedb8082b232ce89dd449d869fb54b98.css\" integrity=\"sha256-S9K9jZr6nSyYicYad3JdiTKrvsstXZrvYqmLUX9i3tc= sha512-CDGjy3xeyiqBgUMa+GelihW394pqAARXwsU+HIiOotlnp1sLBVgO6v2ZszL0arwKU8CpvL9wHyLYBIdfX92YbQ==\" >
+
+
+            <link rel=\"shortcut icon\" href=\"/favicon.ico\" type=\"image/x-icon\">
+            <link rel=\"icon\" href=\"/cargo-835dd6a18132048a52ac569f2615b59d.png\" type=\"image/png\">
+            <link rel=\"search\" href=\"/opensearch.xml\" type=\"application/opensearchdescription+xml\" title=\"Cargo\">
+          </head>
+          <body>
+            <!-- EMBER_CLI_FASTBOOT_BODY -->
+            <noscript>
+                <div id=\"main\">
+                    <div class='noscript'>
+                        This site requires JavaScript to be enabled.
+                    </div>
+                </div>
+            </noscript>
+
+            <script src=\"/assets/vendor-bfe89101b20262535de5a5ccdc276965.js\" integrity=\"sha256-U12Xuwhz1bhJXWyFW/hRr+Wa8B6FFDheTowik5VLkbw= sha512-J/cUUuUN55TrdG8P6Zk3/slI0nTgzYb8pOQlrXfaLgzr9aEumr9D1EzmFyLy1nrhaDGpRN1T8EQrU21Jl81pJQ==\" ></script>
+            <script src=\"/assets/cargo-4023b68501b7b3e17b2bb31f50f5eeea.js\" integrity=\"sha256-9atimKc1KC6HMJF/B07lP3Cjtgr2tmET8Vau0Re5mVI= sha512-XJyBDQU4wtA1aPyPXaFzTE5Wh/mYJwkKHqZ/Fn4p/ezgdKzSCFu6FYn81raBCnCBNsihfhrkb88uF6H5VraHMA==\" ></script>
+
+          </body>
+        </html>
+}";
+    let html: Html = from_str(xml)?;
+    assert_eq!(&html.head.title, "crates.io: Rust Package Registr");
+    Ok(html)
+}
+```
+
 # Features
 
 - `encoding`: support non utf8 xmls
@@ -124,6 +233,10 @@ test bench_quick_xml_namespaced ... bench:     389,977 ns/iter (+/- 32,045)
 
 // same bench with xml-rs
 test bench_xml_rs               ... bench:  14,468,930 ns/iter (+/- 321,171)
+
+// serde-xml-rs vs serialize feature
+test bench_serde_quick_xml      ... bench:   1,181,198 ns/iter (+/- 138,290)
+test bench_serde_xml_rs         ... bench:  15,039,564 ns/iter (+/- 783,485)
 ```
 
 For a feature and performance comparison, you can also have a look at RazrFalcon's [choose-your-xml-rs](https://github.com/RazrFalcon/choose-your-xml-rs).
diff --git a/compare/Cargo.toml b/compare/Cargo.toml
index 022dc08..4753b07 100644
--- a/compare/Cargo.toml
+++ b/compare/Cargo.toml
@@ -7,5 +7,7 @@ edition = "2018"
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 
 [dev-dependencies]
-quick-xml = { path = ".." }
+quick-xml = { path = "..", features = ["serialize"] }
 xml-rs = "0.8.0"
+serde-xml-rs = "0.3.1"
+serde = { version = "1.0.103", features = [ "derive" ] }
diff --git a/compare/benches/bench-serde.rs b/compare/benches/bench-serde.rs
new file mode 100644
index 0000000..3c59d4a
--- /dev/null
+++ b/compare/benches/bench-serde.rs
@@ -0,0 +1,56 @@
+#![feature(test)]
+
+extern crate quick_xml;
+extern crate serde;
+extern crate serde_xml_rs;
+extern crate test;
+
+use serde::Deserialize;
+use test::Bencher;
+
+const SOURCE: &str = include_str!("../../tests/sample_rss.xml");
+
+#[derive(Debug, Deserialize)]
+struct Rss {
+    channel: Channel,
+}
+
+#[derive(Debug, Deserialize)]
+struct Channel {
+    title: String,
+    #[serde(rename = "item", default)]
+    items: Vec<Item>,
+}
+
+#[derive(Debug, Deserialize)]
+struct Item {
+    title: String,
+    link: String,
+    #[serde(rename = "pubDate")]
+    pub_date: String,
+    enclosure: Option<Enclosure>,
+}
+
+#[derive(Debug, Deserialize)]
+struct Enclosure {
+    url: String,
+    length: String,
+    #[serde(rename = "type")]
+    typ: String,
+}
+
+#[bench]
+fn bench_serde_quick_xml(b: &mut Bencher) {
+    b.iter(|| {
+        let rss: Rss = quick_xml::de::from_str(SOURCE).unwrap();
+        assert_eq!(rss.channel.items.len(), 99);
+    });
+}
+
+#[bench]
+fn bench_serde_xml_rs(b: &mut Bencher) {
+    b.iter(|| {
+        let rss: Rss = serde_xml_rs::from_str(SOURCE).unwrap();
+        assert_eq!(rss.channel.items.len(), 99);
+    });
+}
diff --git a/src/de/errors.rs b/src/de/errors.rs
deleted file mode 100644
index 7d16b14..0000000
--- a/src/de/errors.rs
+++ /dev/null
@@ -1,89 +0,0 @@
-use crate::errors::Error;
-use std::fmt;
-
-/// Deserialization error
-#[derive(Debug)]
-pub enum DeError {
-    /// Serde custom error
-    Custom(String),
-    /// Cannot parse to integer
-    Int(std::num::ParseIntError),
-    /// Cannot parse to float
-    Float(std::num::ParseFloatError),
-    /// Xml parsing error
-    Xml(Error),
-    /// Unexpected end of attributes
-    EndOfAttributes,
-    /// Unexpected end of file
-    Eof,
-    /// Invalid value for a boolean
-    InvalidBoolean(String),
-    /// Invalid unit value
-    InvalidUnit(String),
-    /// Invalid event for Enum
-    InvalidEnum(crate::events::Event<'static>),
-    /// Expecting Text event
-    Text,
-    /// Expecting Start event
-    Start,
-    /// Expecting End event
-    End,
-}
-
-impl fmt::Display for DeError {
-    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
-        match self {
-            DeError::Custom(s) => write!(f, "{}", s),
-            DeError::Xml(e) => write!(f, "{}", e),
-            DeError::Int(e) => write!(f, "{}", e),
-            DeError::Float(e) => write!(f, "{}", e),
-            DeError::EndOfAttributes => write!(f, "Unexpected end of attributes"),
-            DeError::Eof => write!(f, "Unexpected `Event::Eof`"),
-            DeError::InvalidBoolean(v) => write!(f, "Invalid boolean value '{}'", v),
-            DeError::InvalidUnit(v) => {
-                write!(f, "Invalid unit value '{}', expected empty string", v)
-            }
-            DeError::InvalidEnum(e) => write!(
-                f,
-                "Invalid event for Enum, expecting Text or Start, got: {:?}",
-                e
-            ),
-            DeError::Text => write!(f, "Expecting Text event"),
-            DeError::Start => write!(f, "Expecting Start event"),
-            DeError::End => write!(f, "Expecting End event"),
-        }
-    }
-}
-
-impl ::std::error::Error for DeError {
-    fn description(&self) -> &str {
-        "xml deserialize error"
-    }
-    fn cause(&self) -> Option<&dyn (::std::error::Error)> {
-        None
-    }
-}
-
-impl serde::de::Error for DeError {
-    fn custom<T: fmt::Display>(msg: T) -> Self {
-        DeError::Custom(msg.to_string())
-    }
-}
-
-impl From<Error> for DeError {
-    fn from(e: Error) -> Self {
-        DeError::Xml(e)
-    }
-}
-
-impl From<std::num::ParseIntError> for DeError {
-    fn from(e: std::num::ParseIntError) -> Self {
-        DeError::Int(e)
-    }
-}
-
-impl From<std::num::ParseFloatError> for DeError {
-    fn from(e: std::num::ParseFloatError) -> Self {
-        DeError::Float(e)
-    }
-}
diff --git a/src/de/escape.rs b/src/de/escape.rs
index 5b5d8cf..a32ad76 100644
--- a/src/de/escape.rs
+++ b/src/de/escape.rs
@@ -1,6 +1,6 @@
 //! Serde `Deserializer` module
 
-use crate::{de::errors::DeError, errors::Error, escape::unescape, reader::Decoder};
+use crate::{errors::serialize::DeError, errors::Error, escape::unescape, reader::Decoder};
 use serde::de::{self, Visitor};
 use serde::{self, forward_to_deserialize_any};
 use std::borrow::Cow;
diff --git a/src/de/map.rs b/src/de/map.rs
index 193c376..334a0f3 100644
--- a/src/de/map.rs
+++ b/src/de/map.rs
@@ -1,7 +1,8 @@
 //! Serde `Deserializer` module
 
 use crate::{
-    de::{errors::DeError, escape::EscapedDeserializer, Deserializer, INNER_VALUE},
+    de::{escape::EscapedDeserializer, Deserializer, INNER_VALUE},
+    errors::serialize::DeError,
     events::{attributes::Attribute, BytesStart, Event},
 };
 use serde::de::{self, DeserializeSeed, IntoDeserializer};
@@ -68,7 +69,7 @@ impl<'a, 'de, R: BufRead> de::MapAccess<'de> for MapAccess<'a, R> {
                     seed.deserialize(INNER_VALUE.into_deserializer()).map(Some)
                 }
                 Some(Event::Start(e)) => {
-                    let name = e.name().to_owned();
+                    let name = e.local_name().to_owned();
                     self.value = MapValue::Nested;
                     seed.deserialize(EscapedDeserializer::new(name, decoder, false))
                         .map(Some)
diff --git a/src/de/mod.rs b/src/de/mod.rs
index ecf5863..4f07303 100644
--- a/src/de/mod.rs
+++ b/src/de/mod.rs
@@ -8,7 +8,7 @@
 //! // Cargo.toml
 //! // [dependencies]
 //! // serde = { version = "1.0", features = [ "derive" ] }
-//! // quick_xml = "0.17"
+//! // quick_xml = { version = "0.17", features = [ "serialize" ] }
 //! extern crate serde;
 //! extern crate quick_xml;
 //!
@@ -107,13 +107,12 @@
 //! }
 //! ```
 
-mod errors;
 mod escape;
 mod map;
 mod seq;
 mod var;
 
-pub use self::errors::DeError;
+pub use crate::errors::serialize::DeError;
 use crate::{
     events::{BytesStart, BytesText, Event},
     Reader,
@@ -176,7 +175,7 @@ impl<R: BufRead> Deserializer<R> {
         loop {
             let e = self.reader.read_event(buf)?;
             match e {
-                Event::Start(_) | Event::End(_) | Event::Text(_) | Event::Eof => {
+                Event::Start(_) | Event::End(_) | Event::Text(_) | Event::Eof | Event::CData(_) => {
                     return Ok(e.into_owned())
                 }
                 _ => buf.clear(),
@@ -198,13 +197,17 @@ impl<R: BufRead> Deserializer<R> {
 
     fn next_text<'a>(&mut self) -> Result<BytesText<'static>, DeError> {
         match self.next(&mut Vec::new())? {
-            Event::Text(e) => Ok(e),
+            Event::Text(e) | Event::CData(e) => Ok(e),
             Event::Eof => Err(DeError::Eof),
             Event::Start(e) => {
                 // allow one nested level
-                let t = match self.next(&mut Vec::new())? {
-                    Event::Text(t) => t,
+                let inner = self.next(&mut Vec::new())?;
+                let t = match inner {
+                    Event::Text(t) | Event::CData(t) => t,
                     Event::Start(_) => return Err(DeError::Start),
+                    Event::End(end) if end.name() == e.name() => {
+                        return Ok(BytesText::from_escaped(&[] as &[u8]));
+                    }
                     Event::End(_) => return Err(DeError::End),
                     Event::Eof => return Err(DeError::Eof),
                     _ => unreachable!(),
@@ -212,7 +215,10 @@ impl<R: BufRead> Deserializer<R> {
                 self.read_to_end(e.name())?;
                 Ok(t)
             }
-            Event::End(_) => Err(DeError::End),
+            Event::End(e) => {
+                self.peek = Some(Event::End(e));
+                Ok(BytesText::from_escaped(&[] as &[u8]))
+            }
             _ => unreachable!(),
         }
     }
@@ -334,13 +340,13 @@ impl<'de, 'a, R: BufRead> de::Deserializer<'de> for &'a mut Deserializer<R> {
     }
 
     fn deserialize_unit<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value, DeError> {
-        let value = self.next_text()?;
-        if value.is_empty() {
-            visitor.visit_unit()
-        } else {
-            Err(DeError::InvalidUnit(
-                value.unescape_and_decode(&self.reader)?,
-            ))
+        let mut buf = Vec::new();
+        match self.next(&mut buf)? {
+            Event::Start(s) => {
+                self.read_to_end(s.name())?;
+                visitor.visit_unit()
+            }
+            e => Err(DeError::InvalidUnit(format!("{:?}", e))),
         }
     }
 
diff --git a/src/de/var.rs b/src/de/var.rs
index 6ec0564..781c7d9 100644
--- a/src/de/var.rs
+++ b/src/de/var.rs
@@ -1,5 +1,8 @@
-use crate::de::{errors::DeError, escape::EscapedDeserializer, Deserializer};
-use crate::events::Event;
+use crate::{
+    de::{escape::EscapedDeserializer, Deserializer},
+    errors::serialize::DeError,
+    events::Event,
+};
 use serde::de::{self, Deserializer as SerdeDeserializer};
 use std::io::BufRead;
 
diff --git a/src/errors.rs b/src/errors.rs
index 77db519..777e7fb 100644
--- a/src/errors.rs
+++ b/src/errors.rs
@@ -116,3 +116,109 @@ impl std::error::Error for Error {
         }
     }
 }
+
+#[cfg(feature = "serialize")]
+pub mod serialize {
+    //! A module to handle serde (de)serialization errors
+
+    use super::Error;
+    use std::fmt;
+
+    /// (De)serialization error
+    #[derive(Debug)]
+    pub enum DeError {
+        /// Serde custom error
+        Custom(String),
+        /// Cannot parse to integer
+        Int(std::num::ParseIntError),
+        /// Cannot parse to float
+        Float(std::num::ParseFloatError),
+        /// Xml parsing error
+        Xml(Error),
+        /// Unexpected end of attributes
+        EndOfAttributes,
+        /// Unexpected end of file
+        Eof,
+        /// Invalid value for a boolean
+        InvalidBoolean(String),
+        /// Invalid unit value
+        InvalidUnit(String),
+        /// Invalid event for Enum
+        InvalidEnum(crate::events::Event<'static>),
+        /// Expecting Text event
+        Text,
+        /// Expecting Start event
+        Start,
+        /// Expecting End event
+        End,
+        /// Unsupported operation
+        Unsupported(&'static str),
+    }
+
+    impl fmt::Display for DeError {
+        fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
+            match self {
+                DeError::Custom(s) => write!(f, "{}", s),
+                DeError::Xml(e) => write!(f, "{}", e),
+                DeError::Int(e) => write!(f, "{}", e),
+                DeError::Float(e) => write!(f, "{}", e),
+                DeError::EndOfAttributes => write!(f, "Unexpected end of attributes"),
+                DeError::Eof => write!(f, "Unexpected `Event::Eof`"),
+                DeError::InvalidBoolean(v) => write!(f, "Invalid boolean value '{}'", v),
+                DeError::InvalidUnit(v) => {
+                    write!(f, "Invalid unit value '{}', expected empty string", v)
+                }
+                DeError::InvalidEnum(e) => write!(
+                    f,
+                    "Invalid event for Enum, expecting Text or Start, got: {:?}",
+                    e
+                ),
+                DeError::Text => write!(f, "Expecting Text event"),
+                DeError::Start => write!(f, "Expecting Start event"),
+                DeError::End => write!(f, "Expecting End event"),
+                DeError::Unsupported(s) => write!(f, "Unsupported operation {}", s),
+            }
+        }
+    }
+
+    impl ::std::error::Error for DeError {
+        fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+            match self {
+                DeError::Int(e) => Some(e),
+                DeError::Float(e) => Some(e),
+                DeError::Xml(e) => Some(e),
+                _ => None,
+            }
+        }
+    }
+
+    impl serde::de::Error for DeError {
+        fn custom<T: fmt::Display>(msg: T) -> Self {
+            DeError::Custom(msg.to_string())
+        }
+    }
+
+    impl serde::ser::Error for DeError {
+        fn custom<T: fmt::Display>(msg: T) -> Self {
+            DeError::Custom(msg.to_string())
+        }
+    }
+
+    impl From<Error> for DeError {
+        fn from(e: Error) -> Self {
+            DeError::Xml(e)
+        }
+    }
+
+    impl From<std::num::ParseIntError> for DeError {
+        fn from(e: std::num::ParseIntError) -> Self {
+            DeError::Int(e)
+        }
+    }
+
+    impl From<std::num::ParseFloatError> for DeError {
+        fn from(e: std::num::ParseFloatError) -> Self {
+            DeError::Float(e)
+        }
+    }
+}
diff --git a/src/lib.rs b/src/lib.rs
index d392661..9351074 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -107,7 +107,7 @@
 //!
 //! quick-xml supports 2 additional features, non activated by default:
 //! - `encoding`: support non utf8 xmls
-//! - `use-failure`: support for failure chainable error
+//! - `serialize`: support serde `Serialize`/`Deserialize`
 #![deny(missing_docs)]
 #![recursion_limit = "1024"]
 
@@ -123,10 +123,14 @@ mod errors;
 mod escape;
 pub mod events;
 mod reader;
+#[cfg(feature = "serialize")]
+pub mod se;
 mod utils;
 mod writer;
 
 // reexports
+#[cfg(feature = "serialize")]
+pub use errors::serialize::DeError;
 pub use errors::{Error, Result};
 pub use reader::Reader;
 pub use writer::Writer;
diff --git a/src/reader.rs b/src/reader.rs
index 516fcc2..35cb34b 100644
--- a/src/reader.rs
+++ b/src/reader.rs
@@ -85,7 +85,7 @@ pub struct Reader<B: BufRead> {
     opened_starts: Vec<usize>,
     /// a buffer to manage namespaces
     ns_buffer: NamespaceBufferIndex,
-    #[cfg(feature = "encoding_rs")]
+    #[cfg(feature = "encoding")]
     /// the encoding specified in the xml, defaults to utf8
     encoding: &'static Encoding,
 }
@@ -105,7 +105,7 @@ impl<B: BufRead> Reader<B> {
             buf_position: 0,
             check_comments: false,
             ns_buffer: NamespaceBufferIndex::default(),
-            #[cfg(feature = "encoding_rs")]
+            #[cfg(feature = "encoding")]
             encoding: ::encoding_rs::UTF_8,
         }
     }
@@ -236,28 +236,18 @@ impl<B: BufRead> Reader<B> {
 
         // need to read 1 character to decide whether pay special attention to attribute values
         let buf_start = buf.len();
-        let start;
-        loop {
-            // Need to contain the `self.reader.fill_buf()` in a scope lexically separate from the
-            // `self.error()` call because both require `&mut self`.
-            let start_result = {
-                let available = match self.reader.fill_buf() {
-                    Ok(n) if n.is_empty() => return Ok(Event::Eof),
-                    Ok(n) => Ok(n),
-                    Err(ref e) if e.kind() == io::ErrorKind::Interrupted => continue,
-                    Err(e) => Err(e),
-                };
-                // `available` is a non-empty slice => we only need the first byte to decide
-                available.map(|xs| xs[0])
-            };
-
-            // throw the error we couldn't throw in the block above because `self` was sill borrowed
-            start = start_result.map_err(Error::Io)?;
-
-            // We intentionally don't `consume()` the byte, otherwise we would have to handle things
-            // like '<>' here already.
-            break;
-        }
+        let start = loop {
+            match self.reader.fill_buf() {
+                Ok(n) if n.is_empty() => return Ok(Event::Eof),
+                Ok(n) => {
+                    // We intentionally don't `consume()` the byte, otherwise we would have to
+                    // handle things like '<>' here already.
+                    break n[0];
+                }
+                Err(ref e) if e.kind() == io::ErrorKind::Interrupted => continue,
+                Err(e) => return Err(Error::Io(e)),
+            }
+        };
 
         if start != b'/' && start != b'!' && start != b'?' {
             match read_elem_until(&mut self.reader, b'>', buf) {
@@ -382,7 +372,7 @@ impl<B: BufRead> Reader<B> {
                                 return Err(Error::UnexpectedEof("CData".to_string()));
                             }
                             Ok(n) => self.buf_position += n,
-                            Err(e) => return Err(e.into()),
+                            Err(e) => return Err(e),
                         }
                         len = buf.len();
                     }
@@ -405,7 +395,7 @@ impl<B: BufRead> Reader<B> {
                                 count += buf.iter().skip(start).filter(|&&b| b == b'<').count();
                                 count -= 1;
                             }
-                            Err(e) => return Err(e.into()),
+                            Err(e) => return Err(e),
                         }
                     }
                     let len = buf.len();
@@ -423,7 +413,7 @@ impl<B: BufRead> Reader<B> {
 
     /// reads `BytesElement` starting with a `?`,
     /// return `Decl` or `PI` event
-    #[cfg(feature = "encoding_rs")]
+    #[cfg(feature = "encoding")]
     fn read_question_mark<'a, 'b>(&'a mut self, buf: &'b [u8]) -> Result<Event<'b>> {
         let len = buf.len();
         if len > 2 && buf[len - 1] == b'?' {
@@ -445,13 +435,12 @@ impl<B: BufRead> Reader<B> {
 
     /// reads `BytesElement` starting with a `?`,
     /// return `Decl` or `PI` event
-    #[cfg(not(feature = "encoding_rs"))]
+    #[cfg(not(feature = "encoding"))]
     fn read_question_mark<'a, 'b>(&'a mut self, buf: &'b [u8]) -> Result<Event<'b>> {
         let len = buf.len();
         if len > 2 && buf[len - 1] == b'?' {
             if len > 5 && &buf[1..4] == b"xml" && is_whitespace(buf[4]) {
                 let event = BytesDecl::from_start(BytesStart::borrowed(&buf[1..len - 1], 3));
-                // Try getting encoding from the declaration event
                 Ok(Event::Decl(event))
             } else {
                 Ok(Event::PI(BytesText::from_escaped(&buf[1..len - 1])))
@@ -707,7 +696,7 @@ impl<B: BufRead> Reader<B> {
     ///
     /// # Note
     ///
-    /// If you instead want to use XML declared encoding, use the `encoding_rs` feature
+    /// If you instead want to use XML declared encoding, use the `encoding` feature
     #[inline]
     #[cfg(not(feature = "encoding"))]
     pub fn decode<'c>(&self, bytes: &'c [u8]) -> Result<&'c str> {
diff --git a/src/se/mod.rs b/src/se/mod.rs
new file mode 100644
index 0000000..1ea5f35
--- /dev/null
+++ b/src/se/mod.rs
@@ -0,0 +1,358 @@
+//! Module to handle custom serde `Serializer`
+
+mod var;
+
+use self::var::{Map, Struct};
+use crate::{
+    errors::serialize::DeError,
+    events::{BytesEnd, BytesStart, BytesText, Event},
+    writer::Writer,
+};
+use serde::ser::{self, Impossible, Serialize};
+use std::io::Write;
+
+/// Serialize struct into a `Write`r
+pub fn to_writer<W: Write, S: Serialize>(writer: W, value: &S) -> Result<(), DeError> {
+    let mut serializer = Serializer::new(writer);
+    value.serialize(&mut serializer)
+}
+
+/// Serialize struct into a `String`
+pub fn to_string<S: Serialize>(value: &S) -> Result<String, DeError> {
+    let mut writer = Vec::new();
+    to_writer(&mut writer, value)?;
+    let s = String::from_utf8(writer).map_err(|e| crate::errors::Error::Utf8(e.utf8_error()))?;
+    Ok(s)
+}
+
+/// A Serializer
+pub struct Serializer<W: Write> {
+    writer: Writer<W>,
+}
+
+impl<W: Write> Serializer<W> {
+    /// Creates a new `Serializer`
+    pub fn new(writer: W) -> Self {
+        Serializer {
+            writer: Writer::new(writer),
+        }
+    }
+
+    fn write_primitive<P: std::fmt::Display>(
+        &mut self,
+        value: P,
+        escaped: bool,
+    ) -> Result<(), DeError> {
+        let value = value.to_string().into_bytes();
+        let event = if escaped {
+            BytesText::from_escaped(value)
+        } else {
+            BytesText::from_plain(&value)
+        };
+        self.writer.write_event(Event::Text(event))?;
+        Ok(())
+    }
+}
+
+impl<'w, W: Write> ser::Serializer for &'w mut Serializer<W> {
+    type Ok = ();
+    type Error = DeError;
+
+    type SerializeSeq = Impossible<Self::Ok, DeError>;
+    type SerializeTuple = Impossible<Self::Ok, DeError>;
+    type SerializeTupleStruct = Impossible<Self::Ok, DeError>;
+    type SerializeTupleVariant = Impossible<Self::Ok, DeError>;
+    type SerializeMap = Map<'w, W>;
+    type SerializeStruct = Struct<'w, W>;
+    type SerializeStructVariant = Impossible<Self::Ok, DeError>;
+
+    fn serialize_bool(self, v: bool) -> Result<Self::Ok, DeError> {
+        self.write_primitive(if v { "true" } else { "false" }, true)
+    }
+
+    fn serialize_i8(self, v: i8) -> Result<Self::Ok, DeError> {
+        self.write_primitive(v, true)
+    }
+
+    fn serialize_i16(self, v: i16) -> Result<Self::Ok, DeError> {
+        self.write_primitive(v, true)
+    }
+
+    fn serialize_i32(self, v: i32) -> Result<Self::Ok, DeError> {
+        self.write_primitive(v, true)
+    }
+
+    fn serialize_i64(self, v: i64) -> Result<Self::Ok, DeError> {
+        self.write_primitive(v, true)
+    }
+
+    fn serialize_u8(self, v: u8) -> Result<Self::Ok, DeError> {
+        self.write_primitive(v, true)
+    }
+
+    fn serialize_u16(self, v: u16) -> Result<Self::Ok, DeError> {
+        self.write_primitive(v, true)
+    }
+
+    fn serialize_u32(self, v: u32) -> Result<Self::Ok, DeError> {
+        self.write_primitive(v, true)
+    }
+
+    fn serialize_u64(self, v: u64) -> Result<Self::Ok, DeError> {
+        self.write_primitive(v, true)
+    }
+
+    fn serialize_f32(self, v: f32) -> Result<Self::Ok, DeError> {
+        self.write_primitive(v, true)
+    }
+
+    fn serialize_f64(self, v: f64) -> Result<Self::Ok, DeError> {
+        self.write_primitive(v, true)
+    }
+
+    fn serialize_char(self, v: char) -> Result<Self::Ok, DeError> {
+        self.write_primitive(v, false)
+    }
+
+    fn serialize_str(self, value: &str) -> Result<Self::Ok, DeError> {
+        self.write_primitive(value, false)
+    }
+
+    fn serialize_bytes(self, _value: &[u8]) -> Result<Self::Ok, DeError> {
+        // TODO: I imagine you'd want to use base64 here.
+        // Not sure how to roundtrip effectively though...
+        Err(DeError::Unsupported("serialize_bytes"))
+    }
+
+    fn serialize_none(self) -> Result<Self::Ok, DeError> {
+        Ok(())
+    }
+
+    fn serialize_some<T: ?Sized + Serialize>(self, value: &T) -> Result<Self::Ok, DeError> {
+        value.serialize(self)
+    }
+
+    fn serialize_unit(self) -> Result<Self::Ok, DeError> {
+        self.serialize_none()
+    }
+
+    fn serialize_unit_struct(self, name: &'static str) -> Result<Self::Ok, DeError> {
+        self.writer
+            .write_event(Event::Empty(BytesStart::borrowed_name(name.as_bytes())))?;
+        Ok(())
+    }
+
+    fn serialize_unit_variant(
+        self,
+        _name: &'static str,
+        _variant_index: u32,
+        _variant: &'static str,
+    ) -> Result<Self::Ok, DeError> {
+        Err(DeError::Unsupported("serialize_unit_variant"))
+    }
+
+    fn serialize_newtype_struct<T: ?Sized + Serialize>(
+        self,
+        _name: &'static str,
+        _value: &T,
+    ) -> Result<Self::Ok, DeError> {
+        Err(DeError::Unsupported("serialize_newtype_struct"))
+    }
+
+    fn serialize_newtype_variant<T: ?Sized + Serialize>(
+        self,
+        _name: &'static str,
+        _variant_index: u32,
+        variant: &'static str,
+        value: &T,
+    ) -> Result<Self::Ok, DeError> {
+        self.writer
+            .write_event(Event::Start(BytesStart::borrowed_name(variant.as_bytes())))?;
+        value.serialize(&mut *self)?;
+        self.writer
+            .write_event(Event::End(BytesEnd::borrowed(variant.as_bytes())))?;
+        Ok(())
+    }
+
+    fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq, DeError> {
+        // TODO: Figure out how to constrain the things written to only be composites
+        Err(DeError::Unsupported("serialize_seq"))
+    }
+
+    fn serialize_tuple(self, _len: usize) -> Result<Self::SerializeTuple, DeError> {
+        Err(DeError::Unsupported("serialize_tuple"))
+    }
+
+    fn serialize_tuple_struct(
+        self,
+        _name: &'static str,
+        _len: usize,
+    ) -> Result<Self::SerializeTupleStruct, DeError> {
+        Err(DeError::Unsupported("serialize_tuple_struct"))
+    }
+
+    fn serialize_tuple_variant(
+        self,
+        _name: &'static str,
+        _variant_index: u32,
+        _variant: &'static str,
+        _len: usize,
+    ) -> Result<Self::SerializeTupleVariant, DeError> {
+        Err(DeError::Unsupported("serialize_tuple_variant"))
+    }
+
+    fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap, DeError> {
+        Ok(Map::new(self))
+    }
+
+    fn serialize_struct(
+        self,
+        name: &'static str,
+        _len: usize,
+    ) -> Result<Self::SerializeStruct, DeError> {
+        self.writer
+            .write_event(Event::Start(BytesStart::borrowed_name(name.as_bytes())))?;
+        Ok(Struct::new(self, name))
+    }
+
+    fn serialize_struct_variant(
+        self,
+        _name: &'static str,
+        _variant_index: u32,
+        _variant: &'static str,
+        _len: usize,
+    ) -> Result<Self::SerializeStructVariant, DeError> {
+        Err(DeError::Unsupported("serialize_struct_variant"))
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use serde::ser::{SerializeMap, SerializeStruct};
+    use serde::{Serialize, Serializer as SerSerializer};
+
+    #[test]
+    fn test_serialize_bool() {
+        let inputs = vec![(true, "true"), (false, "false")];
+
+        for (src, should_be) in inputs {
+            let mut buffer = Vec::new();
+
+            {
+                let mut ser = Serializer::new(&mut buffer);
+                ser.serialize_bool(src).unwrap();
+            }
+
+            let got = String::from_utf8(buffer).unwrap();
+            assert_eq!(got, should_be);
+        }
+    }
+
+    #[test]
+    fn test_start_serialize_struct() {
+        let mut buffer = Vec::new();
+
+        {
+            let mut ser = Serializer::new(&mut buffer);
+            let _ = ser.serialize_struct("foo", 0).unwrap();
+        }
+
+        let got = String::from_utf8(buffer).unwrap();
+        assert_eq!(got, "<foo>");
+    }
+
+    #[test]
+    fn test_serialize_struct_field() {
+        let mut buffer = Vec::new();
+
+        {
+            let mut ser = Serializer::new(&mut buffer);
+            let mut struct_ser = Struct::new(&mut ser, "baz");
+            struct_ser.serialize_field("foo", "bar").unwrap();
+        }
+
+        let got = String::from_utf8(buffer).unwrap();
+        assert_eq!(got, "<foo>bar</foo>");
+    }
+
+    #[test]
+    fn test_serialize_struct() {
+        #[derive(Serialize)]
+        struct Person {
+            name: String,
+            age: u32,
+        }
+
+        let bob = Person {
+            name: "Bob".to_string(),
+            age: 42,
+        };
+        let should_be = "<Person><name>Bob</name><age>42</age></Person>";
+        let mut buffer = Vec::new();
+
+        {
+            let mut ser = Serializer::new(&mut buffer);
+            bob.serialize(&mut ser).unwrap();
+        }
+
+        let got = String::from_utf8(buffer).unwrap();
+        assert_eq!(got, should_be);
+    }
+
+    #[test]
+    fn test_serialize_map_entries() {
+        let should_be = "<name>Bob</name><age>5</age>";
+        let mut buffer = Vec::new();
+
+        {
+            let mut ser = Serializer::new(&mut buffer);
+            let mut map = Map::new(&mut ser);
+            map.serialize_entry("name", "Bob").unwrap();
+            map.serialize_entry("age", "5").unwrap();
+        }
+
+        let got = String::from_utf8(buffer).unwrap();
+        assert_eq!(got, should_be);
+    }
+
+    #[test]
+    fn test_serialize_enum() {
+        #[derive(Serialize)]
+        #[allow(dead_code)]
+        enum Node {
+            Boolean(bool),
+            Number(f64),
+            String(String),
+        }
+
+        let mut buffer = Vec::new();
+        let should_be = "<Boolean>true</Boolean>";
+
+        {
+            let mut ser = Serializer::new(&mut buffer);
+            let node = Node::Boolean(true);
+            node.serialize(&mut ser).unwrap();
+        }
+
+        let got = String::from_utf8(buffer).unwrap();
+        assert_eq!(got, should_be);
+    }
+
+    #[test]
+    #[ignore]
+    fn serialize_a_list() {
+        let inputs = vec![1, 2, 3, 4];
+
+        let mut buffer = Vec::new();
+
+        {
+            let mut ser = Serializer::new(&mut buffer);
+            inputs.serialize(&mut ser).unwrap();
+        }
+
+        let got = String::from_utf8(buffer).unwrap();
+        println!("{}", got);
+        panic!();
+    }
+}
diff --git a/src/se/var.rs b/src/se/var.rs
new file mode 100644
index 0000000..e60b75b
--- /dev/null
+++ b/src/se/var.rs
@@ -0,0 +1,116 @@
+use crate::{
+    errors::{serialize::DeError, Error},
+    events::{BytesEnd, BytesStart, Event},
+    se::Serializer,
+};
+use serde::ser::{self, Serialize};
+use std::io::Write;
+
+/// An implementation of `SerializeMap` for serializing to XML.
+pub struct Map<'w, W>
+where
+    W: 'w + Write,
+{
+    parent: &'w mut Serializer<W>,
+}
+
+impl<'w, W> Map<'w, W>
+where
+    W: 'w + Write,
+{
+    /// Create a new Map
+    pub fn new(parent: &'w mut Serializer<W>) -> Map<'w, W> {
+        Map { parent }
+    }
+}
+
+impl<'w, W> ser::SerializeMap for Map<'w, W>
+where
+    W: 'w + Write,
+{
+    type Ok = ();
+    type Error = DeError;
+
+    fn serialize_key<T: ?Sized + Serialize>(&mut self, _: &T) -> Result<(), DeError> {
+        Err(DeError::Unsupported(
+            "impossible to serialize the key on its own, please use serialize_entry()",
+        ))
+    }
+
+    fn serialize_value<T: ?Sized + Serialize>(&mut self, value: &T) -> Result<(), DeError> {
+        value.serialize(&mut *self.parent)
+    }
+
+    fn end(self) -> Result<Self::Ok, DeError> {
+        Ok(())
+    }
+
+    fn serialize_entry<K: ?Sized + Serialize, V: ?Sized + Serialize>(
+        &mut self,
+        key: &K,
+        value: &V,
+    ) -> Result<(), DeError> {
+        // TODO: Is it possible to ensure our key is never a composite type?
+        // Anything which isn't a "primitive" would lead to malformed XML here...
+        write!(self.parent.writer.inner(), "<").map_err(Error::Io)?;
+        key.serialize(&mut *self.parent)?;
+        write!(self.parent.writer.inner(), ">").map_err(Error::Io)?;
+
+        value.serialize(&mut *self.parent)?;
+
+        write!(self.parent.writer.inner(), "</").map_err(Error::Io)?;
+        key.serialize(&mut *self.parent)?;
+        write!(self.parent.writer.inner(), ">").map_err(Error::Io)?;
+        Ok(())
+    }
+}
+
+/// An implementation of `SerializeStruct` for serializing to XML.
+pub struct Struct<'w, W>
+where
+    W: 'w + Write,
+{
+    parent: &'w mut Serializer<W>,
+    name: &'w str,
+}
+
+impl<'w, W> Struct<'w, W>
+where
+    W: 'w + Write,
+{
+    /// Create a new `Struct`
+    pub fn new(parent: &'w mut Serializer<W>, name: &'w str) -> Struct<'w, W> {
+        Struct { parent, name }
+    }
+}
+
+impl<'w, W> ser::SerializeStruct for Struct<'w, W>
+where
+    W: 'w + Write,
+{
+    type Ok = ();
+    type Error = DeError;
+
+    fn serialize_field<T: ?Sized + Serialize>(
+        &mut self,
+        key: &'static str,
+        value: &T,
+    ) -> Result<(), DeError> {
+        let key = key.as_bytes();
+        self.parent
+            .writer
+            .write_event(Event::Start(BytesStart::borrowed_name(key)))?;
+        value.serialize(&mut *self.parent)?;
+        self.parent
+            .writer
+            .write_event(Event::End(BytesEnd::borrowed(key)))?;
+        Ok(())
+    }
+
+    fn end(self) -> Result<Self::Ok, DeError> {
+        self.parent
+            .writer
+            .write_event(Event::End(BytesEnd::borrowed(self.name.as_bytes())))?;
+        Ok(())
+    }
+}
diff --git a/src/writer.rs b/src/writer.rs
index 0092aec..5d9ec23 100644
--- a/src/writer.rs
+++ b/src/writer.rs
@@ -85,6 +85,11 @@ impl<W: Write> Writer<W> {
         self.writer
     }
 
+    /// Get inner writer, keeping ownership
+    pub fn inner(&mut self) -> &mut W {
+        &mut self.writer
+    }
+
     /// Writes the given event to the underlying writer.
     pub fn write_event<'a, E: AsRef<Event<'a>>>(&mut self, event: E) -> Result<usize> {
         let mut next_should_line_break = true;


Test patch:
diff --git a/tests/serde-migrated.rs b/tests/serde-migrated.rs
new file mode 100644
index 0000000..d5c62f4
--- /dev/null
+++ b/tests/serde-migrated.rs
@@ -0,0 +1,1020 @@
+#![cfg(feature = "serialize")]
+
+extern crate quick_xml;
+extern crate serde;
+
+use std::fmt::Debug;
+
+use quick_xml::de::from_str;
+use serde::{de, ser};
+use serde::{Deserialize, Serialize};
+
+#[derive(PartialEq, Debug, Serialize, Deserialize)]
+enum Animal {
+    Dog,
+    Frog(String),
+    Ant(Simple),
+    Cat { age: usize, name: String },
+}
+
+#[derive(PartialEq, Debug, Serialize, Deserialize)]
+struct Simple {
+    a: (),
+    b: usize,
+    c: String,
+    d: Option<String>,
+}
+
+#[derive(PartialEq, Debug, Serialize, Deserialize)]
+struct Inner {
+    a: (),
+    b: (usize, String, i8),
+    c: Vec<String>,
+}
+
+#[derive(PartialEq, Debug, Serialize, Deserialize)]
+struct Outer {
+    inner: Option<Inner>,
+}
+
+fn test_parse_ok<'a, T: std::fmt::Debug>(errors: &[(&'a str, T)])
+where
+    T: PartialEq + Debug + ser::Serialize + for<'de> de::Deserialize<'de>,
+{
+    for (i, &(s, ref value)) in errors.iter().enumerate() {
+        match from_str::<T>(s) {
+            Ok(v) => assert_eq!(
+                v, *value,
+                "{} error, expected: {:?}, found: {:?}",
+                i, value, v
+            ),
+            Err(e) => panic!("{} error, expected {:?}, found error {}", i, value, e),
+        }
+
+        // // Make sure we can deserialize into an `Element`.
+        // let xml_value: Element = from_str(s).unwrap();
+
+        // // Make sure we can deserialize from an `Element`.
+        // let v: T = from_value(xml_value.clone()).unwrap();
+        // assert_eq!(v, *value);
+    }
+}
+
+fn test_parse_err<'a, T>(errors: &[&'a str])
+where
+    T: PartialEq + Debug + ser::Serialize + for<'de> de::Deserialize<'de>,
+{
+    for &s in errors {
+        assert!(from_str::<T>(s).is_err());
+    }
+}
+
+fn test_parse_invalid<'a, T>(errors: &[&'a str])
+where
+    T: PartialEq + Debug + ser::Serialize + for<'de> de::Deserialize<'de>,
+{
+    for &s in errors {
+        assert!(from_str::<T>(s).is_err());
+    }
+}
+
+#[test]
+fn test_namespaces() {
+    #[derive(PartialEq, Serialize, Deserialize, Debug)]
+    struct Envelope {
+        subject: String,
+    }
+    let s = r#"
+    <?xml version="1.0" encoding="UTF-8"?>
+    <gesmes:Envelope xmlns:gesmes="http://www.gesmes.org/xml/2002-08-01" xmlns="http://www.ecb.int/vocabulary/2002-08-01/eurofxref">
+        <gesmes:subject>Reference rates</gesmes:subject>
+    </gesmes:Envelope>"#;
+    test_parse_ok(&[(
+        s,
+        Envelope {
+            subject: "Reference rates".to_string(),
+        },
+    )]);
+}
+
+#[test]
+#[ignore] // FIXME
+fn test_doctype() {
+    #[derive(PartialEq, Serialize, Deserialize, Debug)]
+    struct Envelope {
+        subject: String,
+    }
+
+    test_parse_ok(&[
+        (
+            r#"
+            <?xml version="1.0" encoding="UTF-8"?>
+            <!DOCTYPE Envelope>
+            <Envelope>
+            <subject>Reference rates</subject>
+            </Envelope>"#,
+            Envelope {
+                subject: "Reference rates".to_string(),
+            },
+        ),
+        (
+            r#"
+            <?xml version="1.0" encoding="UTF-8"?>
+            <!DOCTYPE Envelope[]>
+            <Envelope>
+            <subject>Reference rates</subject>
+            </Envelope>"#,
+            Envelope {
+                subject: "Reference rates".to_string(),
+            },
+        ),
+        (
+            r#"
+            <?xml version="1.0" encoding="UTF-8"?>
+            <!DOCTYPE Envelope [
+                <!ELEMENT subject (#PCDATA)>
+            ] >
+            <Envelope>
+            <subject>Reference rates</subject>
+            </Envelope>"#,
+            Envelope {
+                subject: "Reference rates".to_string(),
+            },
+        ),
+    ]);
+}
+
+// pass in quick-xml because there is no proper doctype support
+//
+//
+// #[test]
+// fn test_doctype_fail() {
+//     #[derive(PartialEq, Serialize, Deserialize, Debug)]
+//     struct Envelope {
+//         subject: String,
+//     }
+//
+//     test_parse_err::<Envelope>(&[
+//         r#"
+//             <?xml version="1.0" encoding="UTF-8"?>
+//             <!DOCTYPE Envelope [
+//                 <!ELEMENT subject (#PCDATA)>
+//             >
+//             <Envelope>
+//             <subject>Reference rates</subject>
+//             </Envelope>"#,
+//         r#"
+//             <?xml version="1.0" encoding="UTF-8"?>
+//             <Envelope>
+//             <subject>Reference rates</subject>
+//             <!DOCTYPE Envelope [
+//                 <!ELEMENT subject (#PCDATA)>
+//             ]>
+//             </Envelope>"#,
+//     ])
+// }
+
+#[test]
+#[ignore] // FIXME
+fn test_forwarded_namespace() {
+    #[derive(PartialEq, Serialize, Deserialize, Debug)]
+    struct Graphml {
+        #[serde(rename = "xsi:schemaLocation")]
+        schema_location: String,
+    }
+    let s = r#"
+    <?xml version="1.0" encoding="UTF-8"?>
+    <graphml xmlns="http://graphml.graphdrawing.org/xmlns"
+        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+        xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns
+        http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd">
+    </graphml>"#;
+    test_parse_ok(&[(
+        s,
+        Graphml {
+            schema_location: "http://graphml.graphdrawing.org/xmlns
+        http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd"
+                .to_string(),
+        },
+    )]);
+}
+
+#[test]
+fn test_parse_string() {
+    test_parse_ok(&[
+        (
+            "<bla>This is a String</bla>",
+            "This is a String".to_string(),
+        ),
+        ("<bla></bla>", "".to_string()),
+        ("<bla>     </bla>", "".to_string()),
+        ("<bla>&lt;boom/&gt;</bla>", "<boom/>".to_string()),
+        ("<bla>&#9835;</bla>", "♫".to_string()),
+        ("<bla>&#x266B;</bla>", "♫".to_string()),
+        //(
+        //    "<bla>♫<![CDATA[<cookies/>]]>♫</bla>",
+        //    "♫<cookies/>♫".to_string(),
+        //),
+    ]);
+}
+
+#[test]
+#[ignore] // FIXME
+fn test_parse_string_not_trim() {
+    test_parse_ok(&[("<bla>     </bla>", "     ".to_string())]);
+}
+
+#[test]
+#[ignore] // FIXME
+fn test_parse_enum() {
+    use self::Animal::*;
+
+    test_parse_ok(&[
+        ("<Animal xsi:type=\"Dog\"/>", Dog),
+        (
+            "<Animal xsi:type=\"Frog\">Quak</Animal>",
+            Frog("Quak".to_string()),
+        ),
+        (
+            "<Animal xsi:type=\"Ant\"><a/><c>bla</c><b>15</b><d>Foo</d></Animal>",
+            Ant(Simple {
+                a: (),
+                b: 15,
+                c: "bla".to_string(),
+                d: Some("Foo".to_string()),
+            }),
+        ),
+        (
+            "<Animal xsi:type=\"Ant\"><a/><c>bla</c><b>15</b></Animal>",
+            Ant(Simple {
+                a: (),
+                b: 15,
+                c: "bla".to_string(),
+                d: None,
+            }),
+        ),
+        (
+            "<Animal xsi:type=\"Cat\"><age>42</age><name>Shere Khan</name></Animal>",
+            Cat {
+                age: 42,
+                name: "Shere Khan".to_string(),
+            },
+        ),
+    ]);
+
+    #[derive(PartialEq, Debug, Serialize, Deserialize)]
+    struct Helper {
+        x: Animal,
+    }
+
+    test_parse_ok(&[
+        ("<Helper><x xsi:type=\"Dog\"/></Helper>", Helper { x: Dog }),
+        (
+            "<Helper><x xsi:type=\"Frog\">Quak</Animal></Helper>",
+            Helper {
+                x: Frog("Quak".to_string()),
+            },
+        ),
+        (
+            "<Helper><x xsi:type=\"Cat\">
+                <age>42</age>
+                <name>Shere Khan</name>
+            </x></Helper>",
+            Helper {
+                x: Cat {
+                    age: 42,
+                    name: "Shere Khan".to_string(),
+                },
+            },
+        ),
+    ]);
+}
+
+#[test]
+fn test_parse_i64() {
+    test_parse_ok(&[
+        ("<bla>0</bla>", 0),
+        ("<bla>-2</bla>", -2),
+        ("<bla>-1234</bla>", -1234),
+        ("<bla> -1234 </bla>", -1234),
+    ]);
+}
+
+#[test]
+fn test_parse_u64() {
+    test_parse_ok(&[
+        ("<bla>0</bla>", 0),
+        ("<bla>1234</bla>", 1234),
+        ("<bla> 1234 </bla>", 1234),
+    ]);
+}
+
+#[test]
+fn test_parse_bool() {
+    test_parse_ok(&[
+        ("<bla>true</bla>", true),
+        ("<bla>false</bla>", false),
+        ("<bla> true </bla>", true),
+        ("<bla> false </bla>", false),
+        ("<bla>1</bla>", true),
+        ("<bla>0</bla>", false),
+    ]);
+
+    test_parse_invalid::<bool>(&["<bla>verum</bla>"]);
+}
+
+#[test]
+fn test_parse_unit() {
+    test_parse_ok(&[("<bla/>", ())]);
+}
+
+#[test]
+fn test_parse_f64() {
+    test_parse_ok(&[
+        ("<bla>3.0</bla>", 3.0f64),
+        ("<bla>3.1</bla>", 3.1),
+        ("<bla>-1.2</bla>", -1.2),
+        ("<bla>0.4</bla>", 0.4),
+        ("<bla>0.4e5</bla>", 0.4e5),
+        ("<bla>0.4e15</bla>", 0.4e15),
+        ("<bla>0.4e-01</bla>", 0.4e-01), // precision troubles
+        ("<bla> 0.4e-01 </bla>", 0.4e-01),
+    ]);
+}
+
+#[test]
+fn test_parse_struct() {
+    test_parse_ok(&[
+        (
+            "<Simple>
+                <c>abc</c>
+                <a/>
+                <b>2</b>
+            </Simple>",
+            Simple {
+                a: (),
+                b: 2,
+                c: "abc".to_string(),
+                d: None,
+            },
+        ),
+        (
+            "<Simple><!-- this is a comment -->
+                <c>abc</c>
+                <a/>
+                <b>2</b>
+            </Simple>",
+            Simple {
+                a: (),
+                b: 2,
+                c: "abc".to_string(),
+                d: None,
+            },
+        ),
+        (
+            "<Simple d=\"Foo\"><!-- this is a comment -->
+                <c>abc</c>
+                <a/>
+                <b>2</b>
+            </Simple>",
+            Simple {
+                a: (),
+                b: 2,
+                c: "abc".to_string(),
+                d: Some("Foo".to_string()),
+            },
+        ),
+    ]);
+}
+
+#[test]
+fn test_option() {
+    test_parse_ok(&[
+        ("<a/>", Some("".to_string())),
+        ("<a></a>", Some("".to_string())),
+        ("<a> </a>", Some("".to_string())),
+        ("<a>42</a>", Some("42".to_string())),
+    ]);
+}
+
+#[test]
+#[ignore] // FIXME
+fn test_option_not_trim() {
+    test_parse_ok(&[("<a> </a>", Some(" ".to_string()))]);
+}
+
+#[test]
+fn test_amoskvin() {
+    #[derive(Debug, Deserialize, PartialEq, Serialize)]
+    struct Root {
+        foo: Vec<Foo>,
+    }
+
+    #[derive(Debug, Deserialize, PartialEq, Serialize)]
+    struct Foo {
+        a: String,
+        b: Option<String>,
+    }
+    test_parse_ok(&[(
+        "
+<root>
+<foo>
+ <a>Hello</a>
+ <b>World</b>
+</foo>
+<foo>
+ <a>Hi</a>
+</foo>
+</root>",
+        Root {
+            foo: vec![
+                Foo {
+                    a: "Hello".to_string(),
+                    b: Some("World".to_string()),
+                },
+                Foo {
+                    a: "Hi".to_string(),
+                    b: None,
+                },
+            ],
+        },
+    )]);
+}
+
+#[test]
+#[ignore] // FIXME
+fn test_nicolai86() {
+    #[derive(Serialize, Deserialize, PartialEq, Debug)]
+    struct TheSender {
+        name: String,
+    }
+
+    #[derive(Serialize, Deserialize, PartialEq, Debug)]
+    struct CurrencyCube {
+        currency: String,
+        rate: String,
+    }
+
+    #[derive(Serialize, Deserialize, PartialEq, Debug)]
+    #[allow(non_snake_case)]
+    struct InnerCube {
+        Cube: Vec<CurrencyCube>,
+    }
+
+    #[derive(Serialize, Deserialize, PartialEq, Debug)]
+    #[allow(non_snake_case)]
+    struct OuterCube {
+        Cube: Vec<InnerCube>,
+    }
+
+    #[derive(Serialize, Deserialize, PartialEq, Debug)]
+    #[allow(non_snake_case)]
+    struct Envelope {
+        subject: String,
+        Sender: TheSender,
+        Cube: OuterCube,
+    }
+    test_parse_ok(&[
+        (
+            r#"
+            <?xml version="1.0" encoding="UTF-8"?>
+            <gesmes:Envelope xmlns:gesmes="http://www.gesmes.org/xml/2002-08-01" xmlns="http://www.ecb.int/vocabulary/2002-08-01/eurofxref">
+                <gesmes:subject>Reference rates</gesmes:subject>
+                <gesmes:Sender>
+                    <gesmes:name>European Central Bank</gesmes:name>
+                </gesmes:Sender>
+                <Cube> </Cube>
+            </gesmes:Envelope>"#,
+            Envelope {
+                subject: "Reference rates".to_string(),
+                Sender: TheSender {
+                    name: "European Central Bank".to_string(),
+                },
+                Cube: OuterCube {
+                    Cube: vec![],
+                }
+            },
+        ),
+        (
+            r#"
+            <?xml version="1.0" encoding="UTF-8"?>
+            <gesmes:Envelope xmlns:gesmes="http://www.gesmes.org/xml/2002-08-01" xmlns="http://www.ecb.int/vocabulary/2002-08-01/eurofxref">
+                <gesmes:subject>Reference rates</gesmes:subject>
+                <gesmes:Sender>
+                    <gesmes:name>European Central Bank</gesmes:name>
+                </gesmes:Sender>
+                <Cube><Cube>
+                    <Cube currency='GBP' rate='0.81725'/>
+                    <Cube currency='Latinum' rate='999999'/>
+                </Cube></Cube>
+            </gesmes:Envelope>"#,
+            Envelope {
+                subject: "Reference rates".to_string(),
+                Sender: TheSender {
+                    name: "European Central Bank".to_string(),
+                },
+                Cube: OuterCube {
+                    Cube: vec![InnerCube {
+                        Cube: vec![
+                            CurrencyCube {
+                                currency: "GBP".to_string(),
+                                rate: "0.81725".to_string(),
+                            },
+                            CurrencyCube {
+                                currency: "Latinum".to_string(),
+                                rate: "999999".to_string(),
+                            },
+                        ],
+                    }],
+                }
+            },
+        ),
+    ]);
+}
+
+#[test]
+fn test_hugo_duncan2() {
+    let s = r#"
+    <?xml version="1.0" encoding="UTF-8"?>
+    <DescribeVpcsResponse xmlns="http://ec2.amazonaws.com/doc/2014-10-01/">
+        <requestId>8d521e9a-509e-4ef6-bbb7-9f1ac0d49cd1</requestId>
+        <vpcSet>
+            <item>
+                <vpcId>vpc-ba0d18d8</vpcId>
+                <state>available</state>
+            </item>
+        </vpcSet>
+    </DescribeVpcsResponse>"#;
+    #[derive(PartialEq, Debug, Serialize, Deserialize)]
+    #[allow(non_snake_case)]
+    struct VpcSet {
+        vpcId: String,
+        state: String,
+    }
+
+    #[derive(PartialEq, Debug, Serialize)]
+    struct ItemVec<T>(Vec<T>);
+
+    impl<'de, T: de::Deserialize<'de>> de::Deserialize<'de> for ItemVec<T> {
+        fn deserialize<D>(deserializer: D) -> Result<ItemVec<T>, D::Error>
+        where
+            D: de::Deserializer<'de>,
+        {
+            #[derive(PartialEq, Debug, Serialize, Deserialize)]
+            struct Helper<U> {
+                item: Vec<U>,
+            }
+            let h: Helper<_> = de::Deserialize::deserialize(deserializer)?;
+            Ok(ItemVec(h.item))
+        }
+    }
+    #[derive(PartialEq, Debug, Serialize, Deserialize)]
+    #[allow(non_snake_case)]
+    struct DescribeVpcsResponse {
+        requestId: String,
+        vpcSet: ItemVec<VpcSet>,
+    }
+    test_parse_ok(&[(
+        s,
+        DescribeVpcsResponse {
+            requestId: "8d521e9a-509e-4ef6-bbb7-9f1ac0d49cd1".to_string(),
+            vpcSet: ItemVec(vec![VpcSet {
+                vpcId: "vpc-ba0d18d8".to_string(),
+                state: "available".to_string(),
+            }]),
+        },
+    )]);
+}
+
+#[test]
+fn test_hugo_duncan() {
+    let s = "
+        <?xml version=\"1.0\" encoding=\"UTF-8\"?>
+        <DescribeInstancesResponse xmlns=\"http://ec2.amazonaws.com/doc/2014-10-01/\">
+            <requestId>9474f558-10a5-42e8-84d1-f9ee181fe943</requestId>
+            <reservationSet/>
+        </DescribeInstancesResponse>
+    ";
+    #[derive(PartialEq, Debug, Serialize, Deserialize)]
+    #[allow(non_snake_case)]
+    struct DescribeInstancesResponse {
+        requestId: String,
+        reservationSet: (),
+    }
+    test_parse_ok(&[(
+        s,
+        DescribeInstancesResponse {
+            requestId: "9474f558-10a5-42e8-84d1-f9ee181fe943".to_string(),
+            reservationSet: (),
+        },
+    )]);
+}
+
+#[test]
+fn test_parse_xml_value() {
+    #[derive(Eq, Debug, PartialEq, Deserialize, Serialize)]
+    struct Test {
+        #[serde(rename = "$value")]
+        myval: String,
+    }
+    test_parse_ok(&[(
+        "<Test>abc</Test>",
+        Test {
+            myval: "abc".to_string(),
+        },
+    )]);
+}
+
+#[test]
+#[ignore] // FIXME
+fn test_parse_complexstruct() {
+    test_parse_ok(&[
+        (
+            "<Outer>
+                <inner>
+                    <b>2</b>
+                    <b>boom</b>
+                    <b>88</b>
+                </inner>
+            </Outer>",
+            Outer {
+                inner: Some(Inner {
+                    a: (),
+                    b: (2, "boom".to_string(), 88),
+                    c: vec![],
+                }),
+            },
+        ),
+        (
+            "<Outer>
+                <inner>
+                    <c>abc</c>
+                    <c>xyz</c>
+                    <a/>
+                    <b>2</b>
+                    <b>boom</b>
+                    <b>88</b>
+                </inner>
+            </Outer>",
+            Outer {
+                inner: Some(Inner {
+                    a: (),
+                    b: (2, "boom".to_string(), 88),
+                    c: vec!["abc".to_string(), "xyz".to_string()],
+                }),
+            },
+        ),
+        ("<Outer/>", Outer { inner: None }),
+    ]);
+}
+
+#[test]
+fn test_parse_attributes() {
+    #[derive(PartialEq, Debug, Serialize, Deserialize)]
+    struct A {
+        a1: String,
+        #[serde(rename = "$value")]
+        a2: i32,
+    }
+
+    test_parse_ok(&[(
+        r#"<A a1="What is the answer to the ultimate question?">42</A>"#,
+        A {
+            a1: "What is the answer to the ultimate question?".to_string(),
+            a2: 42,
+        },
+    )]);
+
+    #[derive(PartialEq, Debug, Serialize, Deserialize)]
+    struct B {
+        b1: String,
+        b2: i32,
+    }
+
+    test_parse_ok(&[(
+        r#"<B b1="What is the answer to the ultimate question?" b2="42"/>"#,
+        B {
+            b1: "What is the answer to the ultimate question?".to_string(),
+            b2: 42,
+        },
+    )]);
+
+    #[derive(PartialEq, Debug, Serialize, Deserialize)]
+    struct C {
+        c1: B,
+    }
+
+    test_parse_ok(&[
+        (
+            r#"<C><c1 b1="What is the answer to the ultimate question?" b2="42"/></C>"#,
+            C {
+                c1: B {
+                    b1: "What is the answer to the ultimate question?".to_string(),
+                    b2: 42,
+                },
+            },
+        ),
+        (
+            r#"<C><c1 b1="What is the answer to the ultimate question?" b2="42"/> </C>"#,
+            C {
+                c1: B {
+                    b1: "What is the answer to the ultimate question?".to_string(),
+                    b2: 42,
+                },
+            },
+        ),
+        (
+            r#"<C>  <c1 b1="What is the answer to the ultimate question?" b2="42">
+        </c1> </C>"#,
+            C {
+                c1: B {
+                    b1: "What is the answer to the ultimate question?".to_string(),
+                    b2: 42,
+                },
+            },
+        ),
+    ]);
+
+    #[derive(PartialEq, Debug, Serialize, Deserialize)]
+    struct D {
+        d1: Option<A>,
+    }
+    test_parse_ok(&[(
+        r#"<D><d1 a1="What is the answer to the ultimate question?">42</d1></D>"#,
+        D {
+            d1: Some(A {
+                a1: "What is the answer to the ultimate question?".to_string(),
+                a2: 42,
+            }),
+        },
+    )]);
+}
+
+#[test]
+#[ignore] // FIXME
+fn test_parse_hierarchies() {
+    #[derive(PartialEq, Debug, Serialize, Deserialize)]
+    struct A {
+        a1: String,
+        a2: (String, String),
+    }
+    #[derive(PartialEq, Debug, Serialize, Deserialize)]
+    struct B {
+        b1: A,
+        b2: (A, A),
+    }
+    #[derive(PartialEq, Debug, Serialize, Deserialize)]
+    struct C {
+        c1: B,
+        c2: Vec<B>,
+    }
+
+    test_parse_ok(&[
+        (
+            "<C><c1>
+            <b1>
+                <a1>No</a1>
+                <a2>Maybe</a2>
+                <a2>Yes</a2>
+            </b1>
+            <b2>
+                <a1>Red</a1>
+                <a2>Green</a2>
+                <a2>Blue</a2>
+            </b2>
+            <b2>
+                <a1>London</a1>
+                <a2>Berlin</a2>
+                <a2>Paris</a2>
+            </b2>
+        </c1></C>",
+            C {
+                c1: B {
+                    b1: A {
+                        a1: "No".to_string(),
+                        a2: ("Maybe".to_string(), "Yes".to_string()),
+                    },
+                    b2: (
+                        A {
+                            a1: "Red".to_string(),
+                            a2: ("Green".to_string(), "Blue".to_string()),
+                        },
+                        A {
+                            a1: "London".to_string(),
+                            a2: ("Berlin".to_string(), "Paris".to_string()),
+                        },
+                    ),
+                },
+                c2: vec![],
+            },
+        ),
+        (
+            "<C><c1>
+            <b2>
+                <a2>Green</a2>
+                <a2>Blue</a2>
+                <a1>Red</a1>
+            </b2>
+            <b2>
+                <a2>Berlin</a2>
+                <a2>Paris</a2>
+                <a1>London</a1>
+            </b2>
+            <b1>
+                <a2>Maybe</a2>
+                <a2>Yes</a2>
+                <a1>No</a1>
+            </b1>
+        </c1></C>",
+            C {
+                c1: B {
+                    b1: A {
+                        a1: "No".to_string(),
+                        a2: ("Maybe".to_string(), "Yes".to_string()),
+                    },
+                    b2: (
+                        A {
+                            a1: "Red".to_string(),
+                            a2: ("Green".to_string(), "Blue".to_string()),
+                        },
+                        A {
+                            a1: "London".to_string(),
+                            a2: ("Berlin".to_string(), "Paris".to_string()),
+                        },
+                    ),
+                },
+                c2: vec![],
+            },
+        ),
+    ]);
+}
+
+#[test]
+fn unknown_field() {
+    #[derive(Deserialize, Debug, PartialEq, Eq, Serialize)]
+    struct A {
+        other: Vec<Other>,
+    }
+
+    #[derive(Deserialize, Debug, PartialEq, Eq, Serialize)]
+    struct Other {
+        d: i32,
+    }
+    test_parse_ok(&[(
+        "<a>
+               <b>
+                 <c>5</c>
+               </b>
+               <other>
+                 <d>6</d>
+               </other>
+            </a>",
+        A {
+            other: vec![Other { d: 6 }],
+        },
+    )]);
+}
+
+// #[test]
+// fn eoz() {
+//     use std::io::Read;
+//     let mut file = std::fs::File::open("Report_test.2.xml").unwrap();
+//     let mut s = String::new();
+//     file.read_to_string(&mut s).unwrap();
+
+//     let _xml_value: Element = from_str(&s).unwrap();
+// }
+
+#[test]
+fn test_parse_unfinished() {
+    test_parse_err::<Simple>(&["<Simple>
+            <c>abc</c>
+            <a/>
+            <b>2</b>
+            <d/>"]);
+}
+
+#[test]
+fn test_things_qc_found() {
+    test_parse_err::<u32>(&["<\u{0}:/"]);
+}
+
+#[test]
+fn futile() {
+    #[derive(Eq, PartialEq, Debug, Serialize, Deserialize)]
+    struct Object {
+        id: u8,
+        name: String,
+        x: u8,
+        y: u8,
+        width: u8,
+        height: u8,
+        ellipse: Option<()>,
+    }
+
+    test_parse_ok(&[
+        (
+            r###"
+            <object id="11" name="testEllipse" x="102" y="38" width="21" height="14">
+              <ellipse/>
+            </object>
+            "###,
+            Object {
+                id: 11,
+                name: "testEllipse".to_owned(),
+                x: 102,
+                y: 38,
+                width: 21,
+                height: 14,
+                ellipse: Some(()),
+            },
+        ),
+        (
+            r###"
+            <object id="11" name="testEllipse" x="102" y="38" width="21" height="14">
+            </object>
+            "###,
+            Object {
+                id: 11,
+                name: "testEllipse".to_owned(),
+                x: 102,
+                y: 38,
+                width: 21,
+                height: 14,
+                ellipse: None,
+            },
+        ),
+    ]);
+}
+
+#[test]
+fn futile2() {
+    #[derive(Eq, PartialEq, Debug, Serialize, Deserialize)]
+    struct Null;
+
+    #[derive(Eq, PartialEq, Debug, Serialize, Deserialize)]
+    struct Object {
+        field: Option<Null>,
+    };
+
+    #[derive(Eq, PartialEq, Debug, Serialize, Deserialize)]
+    struct Stuff {
+        stuff_field: Option<Object>,
+    };
+
+    test_parse_ok(&[
+        (
+            r###"
+            <object>
+              <field/>
+            </object>
+            "###,
+            Object { field: Some(Null) },
+        ),
+        (
+            r###"
+            <object>
+            </object>
+            "###,
+            Object { field: None },
+        ),
+    ]);
+
+    test_parse_ok(&[
+        (
+            r###"
+            <object>
+              <stuff_field/>
+            </object>
+            "###,
+            Stuff {
+                stuff_field: Some(Object { field: None }),
+            },
+        ),
+        (
+            r###"
+            <object>
+              <stuff_field>
+                <field/>
+              </stuff_field>
+            </object>
+            "###,
+            Stuff {
+                stuff_field: Some(Object { field: Some(Null) }),
+            },
+        ),
+        (
+            r###"
+            <object>
+            </object>
+            "###,
+            Stuff { stuff_field: None },
+        ),
+        (
+            r###"
+            <object/>
+            "###,
+            Stuff { stuff_field: None },
+        ),
+    ]);
+}
diff --git a/tests/serde_roundtrip.rs b/tests/serde_roundtrip.rs
new file mode 100644
index 0000000..1a837d5
--- /dev/null
+++ b/tests/serde_roundtrip.rs
@@ -0,0 +1,77 @@
+#![cfg(feature = "serialize")]
+
+extern crate quick_xml;
+extern crate serde;
+
+use quick_xml::{de::from_str, se::to_string};
+use serde::{Deserialize, Serialize};
+
+#[derive(Debug, Serialize, Deserialize, PartialEq)]
+struct Item {
+    name: String,
+    source: String,
+}
+
+#[derive(Debug, Serialize, Deserialize, PartialEq)]
+enum Node {
+    Boolean(bool),
+    Identifier { value: String, index: u32 },
+    EOF,
+}
+
+#[derive(Debug, Serialize, Deserialize, PartialEq)]
+struct Nodes {
+    #[serde(rename = "$value")]
+    items: Vec<Node>,
+}
+
+#[test]
+fn basic_struct() {
+    let src = r#"<Item><name>Banana</name><source>Store</source></Item>"#;
+    let should_be = Item {
+        name: "Banana".to_string(),
+        source: "Store".to_string(),
+    };
+
+    let item: Item = from_str(src).unwrap();
+    assert_eq!(item, should_be);
+
+    let reserialized_item = to_string(&item).unwrap();
+    assert_eq!(src, reserialized_item);
+}
+
+#[test]
+#[ignore]
+fn round_trip_list_of_enums() {
+    // Construct some inputs
+    let nodes = Nodes {
+        items: vec![
+            Node::Boolean(true),
+            Node::Identifier {
+                value: "foo".to_string(),
+                index: 5,
+            },
+            Node::EOF,
+        ],
+    };
+
+    let should_be = r#"
+    <Nodes>
+        <Boolean>
+            true
+        </Boolean>
+        <Identifier>
+            <value>foo</value>
+            <index>5</index>
+        </Identifier>
+        <EOF />
+    </Nodes>"#;
+
+    let serialized_nodes = to_string(&nodes).unwrap();
+    assert_eq!(serialized_nodes, should_be);
+
+    // Then turn it back into a `Nodes` struct and make sure it's the same
+    // as the original
+    let deserialized_nodes: Nodes = from_str(serialized_nodes.as_str()).unwrap();
+    assert_eq!(deserialized_nodes, nodes);
+}