Repository: tafia/quick-xml
Pull request #468 (2022-09-01 18:12:10)

Start rewrite serde serialization tests
Our serde serializer a far from ideal, but rewriting it requires a very long commit history, as I think (I already have 20 commits and I suspect that I'll double that count...). Because I'm not a big fan of long PRs, I propose that changes, that rewrites tests without changing behavior. Next tests that I plan to add will test new corrected behavior, that is why this PR could look slightly unfinished.

Added a commit that ensures, that key name is always produces valid XML name for an element


Patch:
diff --git a/Cargo.toml b/Cargo.toml
index 48d7168..204eca2 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -186,6 +186,10 @@ required-features = ["serialize"]
 name = "serde-de"
 required-features = ["serialize"]
 
+[[test]]
+name = "serde-se"
+required-features = ["serialize"]
+
 [[test]]
 name = "serde-migrated"
 required-features = ["serialize"]
diff --git a/Changelog.md b/Changelog.md
index 36db39d..22d6715 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -16,6 +16,11 @@
 
 ### Misc Changes
 
+- [#468]: Content of `DeError::Unsupported` changed from `&'static str` to `Cow<'static, str>`
+- [#468]: Ensure that map keys are restricted to only types that can be serialized as primitives
+
+[#468]: https://github.com/tafia/quick-xml/pull/468
+
 ## 0.24.0 -- 2022-08-28
 
 ### New Features
diff --git a/src/de/escape.rs b/src/de/escape.rs
index 976ab10..2a05a25 100644
--- a/src/de/escape.rs
+++ b/src/de/escape.rs
@@ -81,7 +81,7 @@ impl<'de, 'a> serde::Deserializer<'de> for EscapedDeserializer<'a> {
         V: Visitor<'de>,
     {
         Err(DeError::Unsupported(
-            "binary data content is not supported by XML format",
+            "binary data content is not supported by XML format".into(),
         ))
     }
 
diff --git a/src/de/map.rs b/src/de/map.rs
index 08538ec..76524e6 100644
--- a/src/de/map.rs
+++ b/src/de/map.rs
@@ -752,7 +752,9 @@ where
                         self.map.de.reader.decoder(),
                     )
                     .deserialize_seq(visitor),
-                    e => Err(DeError::Custom(format!("Unsupported event {:?}", e))),
+                    e => Err(DeError::Unsupported(
+                        format!("unsupported event {:?}", e).into(),
+                    )),
                 };
                 // TODO: May be assert that here we expect only matching closing tag?
                 self.map.de.read_to_end(e.name())?;
diff --git a/src/de/mod.rs b/src/de/mod.rs
index a023552..e259336 100644
--- a/src/de/mod.rs
+++ b/src/de/mod.rs
@@ -81,7 +81,7 @@ macro_rules! deserialize_primitives {
         where
             V: Visitor<'de>,
         {
-            Err(DeError::Unsupported("binary data content is not supported by XML format"))
+            Err(DeError::Unsupported("binary data content is not supported by XML format".into()))
         }
 
         /// Forwards deserialization to the [`deserialize_bytes`](#method.deserialize_bytes).
diff --git a/src/de/simple_type.rs b/src/de/simple_type.rs
index 3c2b2ac..1921109 100644
--- a/src/de/simple_type.rs
+++ b/src/de/simple_type.rs
@@ -47,7 +47,7 @@ macro_rules! unsupported {
             $($(_: $type,)*)?
             _visitor: V
         ) -> Result<V::Value, Self::Error> {
-            Err(DeError::Unsupported($message))
+            Err(DeError::Unsupported($message.into()))
         }
     };
 }
@@ -345,7 +345,7 @@ impl<'de> VariantAccess<'de> for AtomicUnitOnly {
         T: DeserializeSeed<'de>,
     {
         Err(DeError::Unsupported(
-            "enum newtype variants are not supported as `xs:list` items",
+            "enum newtype variants are not supported as `xs:list` items".into(),
         ))
     }
 
@@ -354,7 +354,7 @@ impl<'de> VariantAccess<'de> for AtomicUnitOnly {
         V: Visitor<'de>,
     {
         Err(DeError::Unsupported(
-            "enum tuple variants are not supported as `xs:list` items",
+            "enum tuple variants are not supported as `xs:list` items".into(),
         ))
     }
 
@@ -367,7 +367,7 @@ impl<'de> VariantAccess<'de> for AtomicUnitOnly {
         V: Visitor<'de>,
     {
         Err(DeError::Unsupported(
-            "enum struct variants are not supported as `xs:list` items",
+            "enum struct variants are not supported as `xs:list` items".into(),
         ))
     }
 }
@@ -648,7 +648,7 @@ impl<'de, 'a> Deserializer<'de> for SimpleTypeDeserializer<'de, 'a> {
         V: Visitor<'de>,
     {
         Err(DeError::Unsupported(
-            "binary data content is not supported by XML format",
+            "binary data content is not supported by XML format".into(),
         ))
     }
 
@@ -796,7 +796,7 @@ impl<'de> VariantAccess<'de> for SimpleTypeUnitOnly {
         T: DeserializeSeed<'de>,
     {
         Err(DeError::Unsupported(
-            "enum newtype variants are not supported for XSD `simpleType`s",
+            "enum newtype variants are not supported for XSD `simpleType`s".into(),
         ))
     }
 
@@ -805,7 +805,7 @@ impl<'de> VariantAccess<'de> for SimpleTypeUnitOnly {
         V: Visitor<'de>,
     {
         Err(DeError::Unsupported(
-            "enum tuple variants are not supported for XSD `simpleType`s",
+            "enum tuple variants are not supported for XSD `simpleType`s".into(),
         ))
     }
 
@@ -818,7 +818,7 @@ impl<'de> VariantAccess<'de> for SimpleTypeUnitOnly {
         V: Visitor<'de>,
     {
         Err(DeError::Unsupported(
-            "enum struct variants are not supported for XSD `simpleType`s",
+            "enum struct variants are not supported for XSD `simpleType`s".into(),
         ))
     }
 }
diff --git a/src/de/var.rs b/src/de/var.rs
index 513e896..2034ced 100644
--- a/src/de/var.rs
+++ b/src/de/var.rs
@@ -43,7 +43,7 @@ where
             }
             _ => {
                 return Err(DeError::Unsupported(
-                    "Invalid event for Enum, expecting `Text` or `Start`",
+                    "Invalid event for Enum, expecting `Text` or `Start`".into(),
                 ))
             }
         };
diff --git a/src/errors.rs b/src/errors.rs
index 0f23590..57f8f3b 100644
--- a/src/errors.rs
+++ b/src/errors.rs
@@ -3,6 +3,8 @@
 use crate::escape::EscapeError;
 use crate::events::attributes::AttrError;
 use crate::utils::write_byte_string;
+use std::fmt;
+use std::io::Error as IoError;
 use std::str::Utf8Error;
 use std::string::FromUtf8Error;
 
@@ -10,7 +12,7 @@ use std::string::FromUtf8Error;
 #[derive(Debug)]
 pub enum Error {
     /// IO error
-    Io(::std::io::Error),
+    Io(IoError),
     /// Input decoding error. If `encoding` feature is disabled, contains `None`,
     /// otherwise contains the UTF-8 decoding error
     NonDecodable(Option<Utf8Error>),
@@ -39,10 +41,10 @@ pub enum Error {
     UnknownPrefix(Vec<u8>),
 }
 
-impl From<::std::io::Error> for Error {
+impl From<IoError> for Error {
     /// Creates a new `Error::Io` from the given error
     #[inline]
-    fn from(error: ::std::io::Error) -> Error {
+    fn from(error: IoError) -> Error {
         Error::Io(error)
     }
 }
@@ -81,8 +83,8 @@ impl From<AttrError> for Error {
 /// A specialized `Result` type where the error is hard-wired to [`Error`].
 pub type Result<T> = std::result::Result<T, Error>;
 
-impl std::fmt::Display for Error {
-    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+impl fmt::Display for Error {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         match self {
             Error::Io(e) => write!(f, "I/O error: {}", e),
             Error::NonDecodable(None) => write!(f, "Malformed input, decoding impossible"),
@@ -132,7 +134,7 @@ pub mod serialize {
 
     use super::*;
     use crate::utils::write_byte_string;
-    use std::fmt;
+    use std::borrow::Cow;
     #[cfg(feature = "overlapped-lists")]
     use std::num::NonZeroUsize;
     use std::num::{ParseFloatError, ParseIntError};
@@ -186,7 +188,7 @@ pub mod serialize {
         /// An attempt to deserialize to a type, that is not supported by the XML
         /// store at current position, for example, attempt to deserialize `struct`
         /// from attribute or attempt to deserialize binary data.
-        Unsupported(&'static str),
+        Unsupported(Cow<'static, str>),
         /// Too many events were skipped while deserializing a sequence, event limit
         /// exceeded. The limit was provided as an argument
         #[cfg(feature = "overlapped-lists")]
@@ -214,7 +216,7 @@ pub mod serialize {
                 }
                 DeError::UnexpectedEof => write!(f, "Unexpected `Event::Eof`"),
                 DeError::ExpectedStart => write!(f, "Expecting `Event::Start`"),
-                DeError::Unsupported(s) => write!(f, "Unsupported operation {}", s),
+                DeError::Unsupported(s) => write!(f, "Unsupported operation: {}", s),
                 #[cfg(feature = "overlapped-lists")]
                 DeError::TooManyEvents(s) => write!(f, "Deserializer buffers {} events, limit exceeded", s),
             }
@@ -292,4 +294,11 @@ pub mod serialize {
             Self::InvalidFloat(e)
         }
     }
+
+    impl From<fmt::Error> for DeError {
+        #[inline]
+        fn from(e: fmt::Error) -> Self {
+            Self::Custom(e.to_string())
+        }
+    }
 }
diff --git a/src/se/key.rs b/src/se/key.rs
new file mode 100644
index 0000000..0adf84a
--- /dev/null
+++ b/src/se/key.rs
@@ -0,0 +1,297 @@
+use crate::errors::serialize::DeError;
+use serde::ser::{Impossible, Serialize, Serializer};
+use serde::serde_if_integer128;
+use std::fmt::Write;
+
+/// A serializer, that ensures, that only plain types can be serialized,
+/// so result can be used as an XML tag or attribute name.
+///
+/// This serializer does not check that name does not contain characters that
+/// [not allowed] in XML names, because in some cases it should pass names
+/// that would be filtered on higher level.
+///
+/// [not allowed]: https://www.w3.org/TR/REC-xml/#sec-common-syn
+pub struct XmlNameSerializer<W: Write> {
+    /// Writer to which this serializer writes content
+    pub writer: W,
+}
+
+impl<W: Write> XmlNameSerializer<W> {
+    #[inline]
+    fn write_str(&mut self, value: &str) -> Result<(), DeError> {
+        Ok(self.writer.write_str(value)?)
+    }
+}
+
+impl<W: Write> Serializer for XmlNameSerializer<W> {
+    type Ok = W;
+    type Error = DeError;
+
+    type SerializeSeq = Impossible<Self::Ok, Self::Error>;
+    type SerializeTuple = Impossible<Self::Ok, Self::Error>;
+    type SerializeTupleStruct = Impossible<Self::Ok, Self::Error>;
+    type SerializeTupleVariant = Impossible<Self::Ok, Self::Error>;
+    type SerializeMap = Impossible<Self::Ok, Self::Error>;
+    type SerializeStruct = Impossible<Self::Ok, Self::Error>;
+    type SerializeStructVariant = Impossible<Self::Ok, Self::Error>;
+
+    write_primitive!();
+
+    fn serialize_str(mut self, value: &str) -> Result<Self::Ok, Self::Error> {
+        self.write_str(value)?;
+        Ok(self.writer)
+    }
+
+    /// We cannot store anything, so the absence of a unit and presence of it
+    /// does not differ, so serialization of unit returns `Err(Unsupported)`
+    fn serialize_unit(self) -> Result<Self::Ok, Self::Error> {
+        Err(DeError::Unsupported(
+            "unit type `()` cannot be serialized as an XML tag name".into(),
+        ))
+    }
+
+    /// We cannot store both a variant discriminant and a variant value,
+    /// so serialization of enum newtype variant returns `Err(Unsupported)`
+    fn serialize_newtype_variant<T: ?Sized + Serialize>(
+        self,
+        name: &'static str,
+        _variant_index: u32,
+        variant: &'static str,
+        _value: &T,
+    ) -> Result<Self::Ok, DeError> {
+        Err(DeError::Unsupported(
+            format!(
+                "enum newtype variant `{}::{}` cannot be serialized as an XML tag name",
+                name, variant
+            )
+            .into(),
+        ))
+    }
+
+    fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
+        Err(DeError::Unsupported(
+            "sequence cannot be serialized as an XML tag name".into(),
+        ))
+    }
+
+    fn serialize_tuple(self, _len: usize) -> Result<Self::SerializeTuple, Self::Error> {
+        Err(DeError::Unsupported(
+            "tuple cannot be serialized as an XML tag name".into(),
+        ))
+    }
+
+    fn serialize_tuple_struct(
+        self,
+        name: &'static str,
+        _len: usize,
+    ) -> Result<Self::SerializeTupleStruct, Self::Error> {
+        Err(DeError::Unsupported(
+            format!(
+                "tuple struct `{}` cannot be serialized as an XML tag name",
+                name
+            )
+            .into(),
+        ))
+    }
+
+    fn serialize_tuple_variant(
+        self,
+        name: &'static str,
+        _variant_index: u32,
+        variant: &'static str,
+        _len: usize,
+    ) -> Result<Self::SerializeTupleVariant, Self::Error> {
+        Err(DeError::Unsupported(
+            format!(
+                "enum tuple variant `{}::{}` cannot be serialized as an XML tag name",
+                name, variant
+            )
+            .into(),
+        ))
+    }
+
+    fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap, Self::Error> {
+        Err(DeError::Unsupported(
+            "map cannot be serialized as an XML tag name".into(),
+        ))
+    }
+
+    fn serialize_struct(
+        self,
+        name: &'static str,
+        _len: usize,
+    ) -> Result<Self::SerializeStruct, Self::Error> {
+        Err(DeError::Unsupported(
+            format!("struct `{}` cannot be serialized as an XML tag name", name).into(),
+        ))
+    }
+
+    fn serialize_struct_variant(
+        self,
+        name: &'static str,
+        _variant_index: u32,
+        variant: &'static str,
+        _len: usize,
+    ) -> Result<Self::SerializeStructVariant, Self::Error> {
+        Err(DeError::Unsupported(
+            format!(
+                "enum struct variant `{}::{}` cannot be serialized as an XML tag name",
+                name, variant
+            )
+            .into(),
+        ))
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use crate::utils::Bytes;
+    use pretty_assertions::assert_eq;
+    use serde::Serialize;
+    use std::collections::BTreeMap;
+
+    #[derive(Debug, Serialize, PartialEq)]
+    struct Unit;
+
+    #[derive(Debug, Serialize, PartialEq)]
+    #[serde(rename = "<\"&'>")]
+    struct UnitEscaped;
+
+    #[derive(Debug, Serialize, PartialEq)]
+    struct Newtype(bool);
+
+    #[derive(Debug, Serialize, PartialEq)]
+    struct Tuple(&'static str, usize);
+
+    #[derive(Debug, Serialize, PartialEq)]
+    struct Struct {
+        key: &'static str,
+        val: usize,
+    }
+
+    #[derive(Debug, Serialize, PartialEq)]
+    enum Enum {
+        Unit,
+        #[serde(rename = "<\"&'>")]
+        UnitEscaped,
+        Newtype(bool),
+        Tuple(&'static str, usize),
+        Struct {
+            key: &'static str,
+            val: usize,
+        },
+    }
+
+    /// Checks that given `$data` successfully serialized as `$expected`
+    macro_rules! serialize_as {
+        ($name:ident: $data:expr => $expected:literal) => {
+            #[test]
+            fn $name() {
+                let ser = XmlNameSerializer {
+                    writer: String::new(),
+                };
+
+                let buffer = $data.serialize(ser).unwrap();
+                assert_eq!(buffer, $expected);
+            }
+        };
+    }
+
+    /// Checks that attempt to serialize given `$data` results to a
+    /// serialization error `$kind` with `$reason`
+    macro_rules! err {
+        ($name:ident: $data:expr => $kind:ident($reason:literal)) => {
+            #[test]
+            fn $name() {
+                let mut buffer = String::new();
+                let ser = XmlNameSerializer {
+                    writer: &mut buffer,
+                };
+
+                match $data.serialize(ser).unwrap_err() {
+                    DeError::$kind(e) => assert_eq!(e, $reason),
+                    e => panic!(
+                        "Expected `{}({})`, found `{:?}`",
+                        stringify!($kind),
+                        $reason,
+                        e
+                    ),
+                }
+                assert_eq!(buffer, "");
+            }
+        };
+    }
+
+    serialize_as!(false_: false => "false");
+    serialize_as!(true_:  true  => "true");
+
+    serialize_as!(i8_:    -42i8                => "-42");
+    serialize_as!(i16_:   -4200i16             => "-4200");
+    serialize_as!(i32_:   -42000000i32         => "-42000000");
+    serialize_as!(i64_:   -42000000000000i64   => "-42000000000000");
+    serialize_as!(isize_: -42000000000000isize => "-42000000000000");
+
+    serialize_as!(u8_:    42u8                => "42");
+    serialize_as!(u16_:   4200u16             => "4200");
+    serialize_as!(u32_:   42000000u32         => "42000000");
+    serialize_as!(u64_:   42000000000000u64   => "42000000000000");
+    serialize_as!(usize_: 42000000000000usize => "42000000000000");
+
+    serde_if_integer128! {
+        serialize_as!(i128_: -420000000000000000000000000000i128 => "-420000000000000000000000000000");
+        serialize_as!(u128_:  420000000000000000000000000000u128 => "420000000000000000000000000000");
+    }
+
+    serialize_as!(f32_: 4.2f32 => "4.2");
+    serialize_as!(f64_: 4.2f64 => "4.2");
+
+    serialize_as!(char_non_escaped: 'h' => "h");
+    serialize_as!(char_lt:   '<' => "<");
+    serialize_as!(char_gt:   '>' => ">");
+    serialize_as!(char_amp:  '&' => "&");
+    serialize_as!(char_apos: '\'' => "'");
+    serialize_as!(char_quot: '"' => "\"");
+
+    serialize_as!(str_valid_name: "valid-name" => "valid-name");
+    serialize_as!(str_space: "string with spaces" => "string with spaces");
+    serialize_as!(str_lt:   "string<" => "string<");
+    serialize_as!(str_gt:   "string>" => "string>");
+    serialize_as!(str_amp:  "string&" => "string&");
+    serialize_as!(str_apos: "string'" => "string'");
+    serialize_as!(str_quot: "string\"" => "string\"");
+
+    err!(bytes: Bytes(b"<\"escaped & bytes'>")
+        => Unsupported("`serialize_bytes` not supported yet"));
+
+    serialize_as!(option_none: Option::<&str>::None => "");
+    serialize_as!(option_some: Some("non-escaped-string") => "non-escaped-string");
+
+    err!(unit: ()
+        => Unsupported("unit type `()` cannot be serialized as an XML tag name"));
+    serialize_as!(unit_struct: Unit => "Unit");
+    serialize_as!(unit_struct_escaped: UnitEscaped => "<\"&'>");
+
+    serialize_as!(enum_unit: Enum::Unit => "Unit");
+    serialize_as!(enum_unit_escaped: Enum::UnitEscaped => "<\"&'>");
+
+    serialize_as!(newtype: Newtype(true) => "true");
+    err!(enum_newtype: Enum::Newtype(false)
+        => Unsupported("enum newtype variant `Enum::Newtype` cannot be serialized as an XML tag name"));
+
+    err!(seq: vec![1, 2, 3]
+        => Unsupported("sequence cannot be serialized as an XML tag name"));
+    err!(tuple: ("<\"&'>", "with\t\r\n spaces", 3usize)
+        => Unsupported("tuple cannot be serialized as an XML tag name"));
+    err!(tuple_struct: Tuple("first", 42)
+        => Unsupported("tuple struct `Tuple` cannot be serialized as an XML tag name"));
+    err!(enum_tuple: Enum::Tuple("first", 42)
+        => Unsupported("enum tuple variant `Enum::Tuple` cannot be serialized as an XML tag name"));
+
+    err!(map: BTreeMap::from([("_1", 2), ("_3", 4)])
+        => Unsupported("map cannot be serialized as an XML tag name"));
+    err!(struct_: Struct { key: "answer", val: 42 }
+        => Unsupported("struct `Struct` cannot be serialized as an XML tag name"));
+    err!(enum_struct: Enum::Struct { key: "answer", val: 42 }
+        => Unsupported("enum struct variant `Enum::Struct` cannot be serialized as an XML tag name"));
+}
diff --git a/src/se/mod.rs b/src/se/mod.rs
index eab194b..42fed72 100644
--- a/src/se/mod.rs
+++ b/src/se/mod.rs
@@ -1,5 +1,83 @@
 //! Module to handle custom serde `Serializer`
 
+/// Implements writing primitives to the underlying writer.
+/// Implementor must provide `write_str(self, &str) -> Result<(), DeError>` method
+macro_rules! write_primitive {
+    ($method:ident ( $ty:ty )) => {
+        fn $method(mut self, value: $ty) -> Result<Self::Ok, Self::Error> {
+            self.write_str(&value.to_string())?;
+            Ok(self.writer)
+        }
+    };
+    () => {
+        fn serialize_bool(mut self, value: bool) -> Result<Self::Ok, Self::Error> {
+            self.write_str(if value { "true" } else { "false" })?;
+            Ok(self.writer)
+        }
+
+        write_primitive!(serialize_i8(i8));
+        write_primitive!(serialize_i16(i16));
+        write_primitive!(serialize_i32(i32));
+        write_primitive!(serialize_i64(i64));
+
+        write_primitive!(serialize_u8(u8));
+        write_primitive!(serialize_u16(u16));
+        write_primitive!(serialize_u32(u32));
+        write_primitive!(serialize_u64(u64));
+
+        serde_if_integer128! {
+            write_primitive!(serialize_i128(i128));
+            write_primitive!(serialize_u128(u128));
+        }
+
+        write_primitive!(serialize_f32(f32));
+        write_primitive!(serialize_f64(f64));
+
+        fn serialize_char(self, value: char) -> Result<Self::Ok, Self::Error> {
+            self.serialize_str(&value.to_string())
+        }
+
+        fn serialize_bytes(self, _value: &[u8]) -> Result<Self::Ok, Self::Error> {
+            //TODO: customization point - allow user to decide how to encode bytes
+            Err(DeError::Unsupported(
+                "`serialize_bytes` not supported yet".into(),
+            ))
+        }
+
+        fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
+            Ok(self.writer)
+        }
+
+        fn serialize_some<T: ?Sized + Serialize>(self, value: &T) -> Result<Self::Ok, Self::Error> {
+            value.serialize(self)
+        }
+
+        fn serialize_unit_struct(self, name: &'static str) -> Result<Self::Ok, Self::Error> {
+            self.serialize_str(name)
+        }
+
+        fn serialize_unit_variant(
+            self,
+            _name: &'static str,
+            _variant_index: u32,
+            variant: &'static str,
+        ) -> Result<Self::Ok, Self::Error> {
+            self.serialize_str(variant)
+        }
+
+        fn serialize_newtype_struct<T: ?Sized + Serialize>(
+            self,
+            _name: &'static str,
+            value: &T,
+        ) -> Result<Self::Ok, Self::Error> {
+            value.serialize(self)
+        }
+    };
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+mod key;
 mod var;
 
 use self::var::{Map, Seq, Struct, Tuple};
@@ -209,7 +287,9 @@ impl<'r, 'w, W: Write> ser::Serializer for &'w mut Serializer<'r, W> {
     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"))
+        Err(DeError::Unsupported(
+            "`serialize_bytes` not supported yet".into(),
+        ))
     }
 
     fn serialize_none(self) -> Result<Self::Ok, DeError> {
@@ -329,873 +409,3 @@ impl<'r, 'w, W: Write> ser::Serializer for &'w mut Serializer<'r, W> {
         Ok(Struct::new(self, variant))
     }
 }
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-    use pretty_assertions::assert_eq;
-    use serde::ser::SerializeMap;
-    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_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\" age=\"42\"/>";
-        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_struct_value_number() {
-        #[derive(Serialize)]
-        struct Person {
-            name: String,
-            #[serde(rename = "$value")]
-            age: u32,
-        }
-
-        let bob = Person {
-            name: "Bob".to_string(),
-            age: 42,
-        };
-        let should_be = "<Person name=\"Bob\">42</Person>";
-        let got = to_string(&bob).unwrap();
-        assert_eq!(got, should_be);
-    }
-
-    #[test]
-    fn test_serialize_struct_value_string() {
-        #[derive(Serialize)]
-        struct Person {
-            name: String,
-            #[serde(rename = "$value")]
-            age: String,
-        }
-
-        let bob = Person {
-            name: "Bob".to_string(),
-            age: "42".to_string(),
-        };
-        let should_be = "<Person name=\"Bob\">42</Person>";
-        let got = to_string(&bob).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!();
-    }
-
-    #[test]
-    fn unit() {
-        #[derive(Serialize)]
-        struct Unit;
-
-        let data = Unit;
-        let should_be = "<root/>";
-        let mut buffer = Vec::new();
-
-        {
-            let mut ser = Serializer::with_root(Writer::new(&mut buffer), Some("root"));
-            data.serialize(&mut ser).unwrap();
-        }
-
-        let got = String::from_utf8(buffer).unwrap();
-        assert_eq!(got, should_be);
-    }
-
-    #[test]
-    fn newtype() {
-        #[derive(Serialize)]
-        struct Newtype(bool);
-
-        let data = Newtype(true);
-        let should_be = "<root>true</root>";
-        let mut buffer = Vec::new();
-
-        {
-            let mut ser = Serializer::with_root(Writer::new(&mut buffer), Some("root"));
-            data.serialize(&mut ser).unwrap();
-        }
-
-        let got = String::from_utf8(buffer).unwrap();
-        assert_eq!(got, should_be);
-    }
-
-    #[test]
-    fn tuple() {
-        let data = (42.0, "answer");
-        let should_be = "<root>42</root><root>answer</root>";
-        let mut buffer = Vec::new();
-
-        {
-            let mut ser =
-                Serializer::with_root(Writer::new_with_indent(&mut buffer, b' ', 4), Some("root"));
-            data.serialize(&mut ser).unwrap();
-        }
-
-        let got = String::from_utf8(buffer).unwrap();
-        assert_eq!(got, should_be);
-    }
-
-    #[test]
-    fn tuple_struct() {
-        #[derive(Serialize)]
-        struct Tuple(f32, &'static str);
-
-        let data = Tuple(42.0, "answer");
-        let should_be = "<root>42</root><root>answer</root>";
-        let mut buffer = Vec::new();
-
-        {
-            let mut ser =
-                Serializer::with_root(Writer::new_with_indent(&mut buffer, b' ', 4), Some("root"));
-            data.serialize(&mut ser).unwrap();
-        }
-
-        let got = String::from_utf8(buffer).unwrap();
-        assert_eq!(got, should_be);
-    }
-
-    #[test]
-    fn struct_() {
-        #[derive(Serialize)]
-        struct Struct {
-            float: f64,
-            string: String,
-        }
-
-        let mut buffer = Vec::new();
-        let should_be = r#"<root float="42" string="answer"/>"#;
-
-        {
-            let mut ser =
-                Serializer::with_root(Writer::new_with_indent(&mut buffer, b' ', 4), Some("root"));
-            let node = Struct {
-                float: 42.0,
-                string: "answer".to_string(),
-            };
-            node.serialize(&mut ser).unwrap();
-        }
-
-        let got = String::from_utf8(buffer).unwrap();
-        assert_eq!(got, should_be);
-    }
-
-    #[test]
-    fn nested_struct() {
-        #[derive(Serialize)]
-        struct Struct {
-            nested: Nested,
-            string: String,
-        }
-
-        #[derive(Serialize)]
-        struct Nested {
-            float: f64,
-        }
-
-        let mut buffer = Vec::new();
-        let should_be = r#"<root string="answer"><nested float="42"/>
-</root>"#;
-
-        {
-            let mut ser =
-                Serializer::with_root(Writer::new_with_indent(&mut buffer, b' ', 4), Some("root"));
-            let node = Struct {
-                nested: Nested { float: 42.0 },
-                string: "answer".to_string(),
-            };
-            node.serialize(&mut ser).unwrap();
-        }
-
-        let got = String::from_utf8(buffer).unwrap();
-        assert_eq!(got, should_be);
-    }
-
-    #[test]
-    fn flatten_struct() {
-        #[derive(Serialize)]
-        struct Struct {
-            #[serde(flatten)]
-            nested: Nested,
-            string: String,
-        }
-
-        #[derive(Serialize)]
-        struct Nested {
-            float: f64,
-        }
-
-        let mut buffer = Vec::new();
-        let should_be = r#"<root><float>42</float><string>answer</string></root>"#;
-
-        {
-            let mut ser =
-                Serializer::with_root(Writer::new_with_indent(&mut buffer, b' ', 4), Some("root"));
-            let node = Struct {
-                nested: Nested { float: 42.0 },
-                string: "answer".to_string(),
-            };
-            node.serialize(&mut ser).unwrap();
-        }
-
-        let got = String::from_utf8(buffer).unwrap();
-        assert_eq!(got, should_be);
-    }
-
-    mod enum_ {
-        use super::*;
-
-        #[derive(Serialize)]
-        struct Nested {
-            float: f64,
-        }
-
-        mod externally_tagged {
-            use super::*;
-            use pretty_assertions::assert_eq;
-
-            #[derive(Serialize)]
-            enum Node {
-                Unit,
-                #[serde(rename = "$primitive=PrimitiveUnit")]
-                PrimitiveUnit,
-                Newtype(bool),
-                Tuple(f64, String),
-                Struct {
-                    float: f64,
-                    string: String,
-                },
-                Holder {
-                    nested: Nested,
-                    string: String,
-                },
-                Flatten {
-                    #[serde(flatten)]
-                    nested: Nested,
-                    string: String,
-                },
-            }
-
-            #[test]
-            fn unit() {
-                let mut buffer = Vec::new();
-                let should_be = "<Unit/>";
-
-                {
-                    let mut ser = Serializer::with_root(Writer::new(&mut buffer), Some("root"));
-                    let node = Node::Unit;
-                    node.serialize(&mut ser).unwrap();
-                }
-
-                let got = String::from_utf8(buffer).unwrap();
-                assert_eq!(got, should_be);
-            }
-
-            #[test]
-            fn primitive_unit() {
-                let mut buffer = Vec::new();
-                let should_be = "PrimitiveUnit";
-
-                {
-                    let mut ser = Serializer::with_root(Writer::new(&mut buffer), Some("root"));
-                    let node = Node::PrimitiveUnit;
-                    node.serialize(&mut ser).unwrap();
-                }
-
-                let got = String::from_utf8(buffer).unwrap();
-                assert_eq!(got, should_be);
-            }
-
-            #[test]
-            fn newtype() {
-                let mut buffer = Vec::new();
-                let should_be = "<Newtype>true</Newtype>";
-
-                {
-                    let mut ser = Serializer::with_root(Writer::new(&mut buffer), Some("root"));
-                    let node = Node::Newtype(true);
-                    node.serialize(&mut ser).unwrap();
-                }
-
-                let got = String::from_utf8(buffer).unwrap();
-                assert_eq!(got, should_be);
-            }
-
-            #[test]
-            fn struct_() {
-                let mut buffer = Vec::new();
-                let should_be = r#"<Struct float="42" string="answer"/>"#;
-
-                {
-                    let mut ser = Serializer::with_root(
-                        Writer::new_with_indent(&mut buffer, b' ', 4),
-                        Some("root"),
-                    );
-                    let node = Node::Struct {
-                        float: 42.0,
-                        string: "answer".to_string(),
-                    };
-                    node.serialize(&mut ser).unwrap();
-                }
-
-                let got = String::from_utf8(buffer).unwrap();
-                assert_eq!(got, should_be);
-            }
-
-            #[test]
-            fn tuple_struct() {
-                let mut buffer = Vec::new();
-                let should_be = "<Tuple>42</Tuple><Tuple>answer</Tuple>";
-
-                {
-                    let mut ser = Serializer::with_root(
-                        Writer::new_with_indent(&mut buffer, b' ', 4),
-                        Some("root"),
-                    );
-                    let node = Node::Tuple(42.0, "answer".to_string());
-                    node.serialize(&mut ser).unwrap();
-                }
-
-                let got = String::from_utf8(buffer).unwrap();
-                assert_eq!(got, should_be);
-            }
-
-            #[test]
-            fn nested_struct() {
-                let mut buffer = Vec::new();
-                let should_be = r#"<Holder string="answer"><nested float="42"/>
-</Holder>"#;
-
-                {
-                    let mut ser = Serializer::with_root(
-                        Writer::new_with_indent(&mut buffer, b' ', 4),
-                        Some("root"),
-                    );
-                    let node = Node::Holder {
-                        nested: Nested { float: 42.0 },
-                        string: "answer".to_string(),
-                    };
-                    node.serialize(&mut ser).unwrap();
-                }
-
-                let got = String::from_utf8(buffer).unwrap();
-                assert_eq!(got, should_be);
-            }
-
-            #[test]
-            fn flatten_struct() {
-                let mut buffer = Vec::new();
-                let should_be = r#"<Flatten><float>42</float><string>answer</string></Flatten>"#;
-
-                {
-                    let mut ser = Serializer::with_root(
-                        Writer::new_with_indent(&mut buffer, b' ', 4),
-                        Some("root"),
-                    );
-                    let node = Node::Flatten {
-                        nested: Nested { float: 42.0 },
-                        string: "answer".to_string(),
-                    };
-                    node.serialize(&mut ser).unwrap();
-                }
-
-                let got = String::from_utf8(buffer).unwrap();
-                assert_eq!(got, should_be);
-            }
-        }
-
-        mod internally_tagged {
-            use super::*;
-            use pretty_assertions::assert_eq;
-
-            #[derive(Serialize)]
-            #[serde(tag = "tag")]
-            enum Node {
-                Unit,
-                /// Primitives (such as `bool`) are not supported by the serde in the internally tagged mode
-                Newtype(NewtypeContent),
-                // Tuple(f64, String),// Tuples are not supported in the internally tagged mode
-                Struct {
-                    float: f64,
-                    string: String,
-                },
-                Holder {
-                    nested: Nested,
-                    string: String,
-                },
-                Flatten {
-                    #[serde(flatten)]
-                    nested: Nested,
-                    string: String,
-                },
-            }
-
-            #[derive(Serialize)]
-            struct NewtypeContent {
-                value: bool,
-            }
-
-            #[test]
-            fn unit() {
-                let mut buffer = Vec::new();
-                let should_be = r#"<root tag="Unit"/>"#;
-
-                {
-                    let mut ser = Serializer::with_root(Writer::new(&mut buffer), Some("root"));
-                    let node = Node::Unit;
-                    node.serialize(&mut ser).unwrap();
-                }
-
-                let got = String::from_utf8(buffer).unwrap();
-                assert_eq!(got, should_be);
-            }
-
-            #[test]
-            fn newtype() {
-                let mut buffer = Vec::new();
-                let should_be = r#"<root tag="Newtype" value="true"/>"#;
-
-                {
-                    let mut ser = Serializer::with_root(Writer::new(&mut buffer), Some("root"));
-                    let node = Node::Newtype(NewtypeContent { value: true });
-                    node.serialize(&mut ser).unwrap();
-                }
-
-                let got = String::from_utf8(buffer).unwrap();
-                assert_eq!(got, should_be);
-            }
-
-            #[test]
-            fn struct_() {
-                let mut buffer = Vec::new();
-                let should_be = r#"<root tag="Struct" float="42" string="answer"/>"#;
-
-                {
-                    let mut ser = Serializer::with_root(
-                        Writer::new_with_indent(&mut buffer, b' ', 4),
-                        Some("root"),
-                    );
-                    let node = Node::Struct {
-                        float: 42.0,
-                        string: "answer".to_string(),
-                    };
-                    node.serialize(&mut ser).unwrap();
-                }
-
-                let got = String::from_utf8(buffer).unwrap();
-                assert_eq!(got, should_be);
-            }
-
-            #[test]
-            fn nested_struct() {
-                let mut buffer = Vec::new();
-                let should_be = r#"<root tag="Holder" string="answer"><nested float="42"/>
-</root>"#;
-
-                {
-                    let mut ser = Serializer::with_root(
-                        Writer::new_with_indent(&mut buffer, b' ', 4),
-                        Some("root"),
-                    );
-                    let node = Node::Holder {
-                        nested: Nested { float: 42.0 },
-                        string: "answer".to_string(),
-                    };
-                    node.serialize(&mut ser).unwrap();
-                }
-
-                let got = String::from_utf8(buffer).unwrap();
-                assert_eq!(got, should_be);
-            }
-
-            #[test]
-            fn flatten_struct() {
-                let mut buffer = Vec::new();
-                let should_be =
-                    r#"<root><tag>Flatten</tag><float>42</float><string>answer</string></root>"#;
-
-                {
-                    let mut ser = Serializer::with_root(
-                        Writer::new_with_indent(&mut buffer, b' ', 4),
-                        Some("root"),
-                    );
-                    let node = Node::Flatten {
-                        nested: Nested { float: 42.0 },
-                        string: "answer".to_string(),
-                    };
-                    node.serialize(&mut ser).unwrap();
-                }
-
-                let got = String::from_utf8(buffer).unwrap();
-                assert_eq!(got, should_be);
-            }
-        }
-
-        mod adjacently_tagged {
-            use super::*;
-            use pretty_assertions::assert_eq;
-
-            #[derive(Serialize)]
-            #[serde(tag = "tag", content = "content")]
-            enum Node {
-                Unit,
-                Newtype(bool),
-                Tuple(f64, String),
-                Struct {
-                    float: f64,
-                    string: String,
-                },
-                Holder {
-                    nested: Nested,
-                    string: String,
-                },
-                Flatten {
-                    #[serde(flatten)]
-                    nested: Nested,
-                    string: String,
-                },
-            }
-
-            #[test]
-            fn unit() {
-                let mut buffer = Vec::new();
-                let should_be = r#"<root tag="Unit"/>"#;
-
-                {
-                    let mut ser = Serializer::with_root(Writer::new(&mut buffer), Some("root"));
-                    let node = Node::Unit;
-                    node.serialize(&mut ser).unwrap();
-                }
-
-                let got = String::from_utf8(buffer).unwrap();
-                assert_eq!(got, should_be);
-            }
-
-            #[test]
-            fn newtype() {
-                let mut buffer = Vec::new();
-                let should_be = r#"<root tag="Newtype" content="true"/>"#;
-
-                {
-                    let mut ser = Serializer::with_root(Writer::new(&mut buffer), Some("root"));
-                    let node = Node::Newtype(true);
-                    node.serialize(&mut ser).unwrap();
-                }
-
-                let got = String::from_utf8(buffer).unwrap();
-                assert_eq!(got, should_be);
-            }
-
-            #[test]
-            fn tuple_struct() {
-                let mut buffer = Vec::new();
-                let should_be = r#"<root tag="Tuple"><content>42</content><content>answer</content>
-</root>"#;
-
-                {
-                    let mut ser = Serializer::with_root(
-                        Writer::new_with_indent(&mut buffer, b' ', 4),
-                        Some("root"),
-                    );
-                    let node = Node::Tuple(42.0, "answer".to_string());
-                    node.serialize(&mut ser).unwrap();
-                }
-
-                let got = String::from_utf8(buffer).unwrap();
-                assert_eq!(got, should_be);
-            }
-
-            #[test]
-            fn struct_() {
-                let mut buffer = Vec::new();
-                let should_be = r#"<root tag="Struct"><content float="42" string="answer"/>
-</root>"#;
-
-                {
-                    let mut ser = Serializer::with_root(
-                        Writer::new_with_indent(&mut buffer, b' ', 4),
-                        Some("root"),
-                    );
-                    let node = Node::Struct {
-                        float: 42.0,
-                        string: "answer".to_string(),
-                    };
-                    node.serialize(&mut ser).unwrap();
-                }
-
-                let got = String::from_utf8(buffer).unwrap();
-                assert_eq!(got, should_be);
-            }
-
-            #[test]
-            fn nested_struct() {
-                let mut buffer = Vec::new();
-                let should_be = r#"<root tag="Holder"><content string="answer"><nested float="42"/></content>
-</root>"#;
-
-                {
-                    let mut ser = Serializer::with_root(
-                        Writer::new_with_indent(&mut buffer, b' ', 4),
-                        Some("root"),
-                    );
-                    let node = Node::Holder {
-                        nested: Nested { float: 42.0 },
-                        string: "answer".to_string(),
-                    };
-                    node.serialize(&mut ser).unwrap();
-                }
-
-                let got = String::from_utf8(buffer).unwrap();
-                assert_eq!(got, should_be);
-            }
-
-            #[test]
-            fn flatten_struct() {
-                let mut buffer = Vec::new();
-                let should_be = r#"<root tag="Flatten"><content><float>42</float><string>answer</string></content>
-</root>"#;
-
-                {
-                    let mut ser = Serializer::with_root(
-                        Writer::new_with_indent(&mut buffer, b' ', 4),
-                        Some("root"),
-                    );
-                    let node = Node::Flatten {
-                        nested: Nested { float: 42.0 },
-                        string: "answer".to_string(),
-                    };
-                    node.serialize(&mut ser).unwrap();
-                }
-
-                let got = String::from_utf8(buffer).unwrap();
-                assert_eq!(got, should_be);
-            }
-        }
-
-        mod untagged {
-            use super::*;
-            use pretty_assertions::assert_eq;
-
-            #[derive(Serialize)]
-            #[serde(untagged)]
-            enum Node {
-                Unit,
-                Newtype(bool),
-                Tuple(f64, String),
-                Struct {
-                    float: f64,
-                    string: String,
-                },
-                Holder {
-                    nested: Nested,
-                    string: String,
-                },
-                Flatten {
-                    #[serde(flatten)]
-                    nested: Nested,
-                    string: String,
-                },
-            }
-
-            #[test]
-            fn unit() {
-                let mut buffer = Vec::new();
-                // Unit variant consists just from the tag, and because tags
-                // are not written in untagged mode, nothing is written
-                let should_be = "";
-
-                {
-                    let mut ser = Serializer::with_root(Writer::new(&mut buffer), Some("root"));
-                    let node = Node::Unit;
-                    node.serialize(&mut ser).unwrap();
-                }
-
-                let got = String::from_utf8(buffer).unwrap();
-                assert_eq!(got, should_be);
-            }
-
-            #[test]
-            fn newtype() {
-                let mut buffer = Vec::new();
-                let should_be = "true";
-
-                {
-                    let mut ser = Serializer::with_root(Writer::new(&mut buffer), Some("root"));
-                    let node = Node::Newtype(true);
-                    node.serialize(&mut ser).unwrap();
-                }
-
-                let got = String::from_utf8(buffer).unwrap();
-                assert_eq!(got, should_be);
-            }
-
-            #[test]
-            fn tuple_struct() {
-                let mut buffer = Vec::new();
-                let should_be = "<root>42</root><root>answer</root>";
-
-                {
-                    let mut ser = Serializer::with_root(
-                        Writer::new_with_indent(&mut buffer, b' ', 4),
-                        Some("root"),
-                    );
-                    let node = Node::Tuple(42.0, "answer".to_string());
-                    node.serialize(&mut ser).unwrap();
-                }
-
-                let got = String::from_utf8(buffer).unwrap();
-                assert_eq!(got, should_be);
-            }
-
-            #[test]
-            fn struct_() {
-                let mut buffer = Vec::new();
-                let should_be = r#"<root float="42" string="answer"/>"#;
-
-                {
-                    let mut ser = Serializer::with_root(
-                        Writer::new_with_indent(&mut buffer, b' ', 4),
-                        Some("root"),
-                    );
-                    let node = Node::Struct {
-                        float: 42.0,
-                        string: "answer".to_string(),
-                    };
-                    node.serialize(&mut ser).unwrap();
-                }
-
-                let got = String::from_utf8(buffer).unwrap();
-                assert_eq!(got, should_be);
-            }
-
-            #[test]
-            fn nested_struct() {
-                let mut buffer = Vec::new();
-                let should_be = r#"<root string="answer"><nested float="42"/>
-</root>"#;
-
-                {
-                    let mut ser = Serializer::with_root(
-                        Writer::new_with_indent(&mut buffer, b' ', 4),
-                        Some("root"),
-                    );
-                    let node = Node::Holder {
-                        nested: Nested { float: 42.0 },
-                        string: "answer".to_string(),
-                    };
-                    node.serialize(&mut ser).unwrap();
-                }
-
-                let got = String::from_utf8(buffer).unwrap();
-                assert_eq!(got, should_be);
-            }
-
-            #[test]
-            fn flatten_struct() {
-                let mut buffer = Vec::new();
-                let should_be = r#"<root><float>42</float><string>answer</string></root>"#;
-
-                {
-                    let mut ser = Serializer::with_root(
-                        Writer::new_with_indent(&mut buffer, b' ', 4),
-                        Some("root"),
-                    );
-                    let node = Node::Flatten {
-                        nested: Nested { float: 42.0 },
-                        string: "answer".to_string(),
-                    };
-                    node.serialize(&mut ser).unwrap();
-                }
-
-                let got = String::from_utf8(buffer).unwrap();
-                assert_eq!(got, should_be);
-            }
-        }
-    }
-}
diff --git a/src/se/var.rs b/src/se/var.rs
index c8e062e..eb15440 100644
--- a/src/se/var.rs
+++ b/src/se/var.rs
@@ -2,6 +2,7 @@ use crate::{
     de::{INNER_VALUE, UNFLATTEN_PREFIX},
     errors::{serialize::DeError, Error},
     events::{BytesEnd, BytesStart, Event},
+    se::key::XmlNameSerializer,
     se::Serializer,
     writer::Writer,
 };
@@ -64,21 +65,27 @@ where
         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)?;
+        let key = key.serialize(XmlNameSerializer {
+            writer: String::new(),
+        })?;
+
+        let writer = self.parent.writer.inner();
+        writer.write_all(b"<").map_err(Error::Io)?;
+        writer.write_all(key.as_bytes()).map_err(Error::Io)?;
+        writer.write_all(b">").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)?;
+        let writer = self.parent.writer.inner();
+        writer.write_all(b"</").map_err(Error::Io)?;
+        writer.write_all(key.as_bytes()).map_err(Error::Io)?;
+        writer.write_all(b">").map_err(Error::Io)?;
         Ok(())
     }
 }
 
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
 /// An implementation of `SerializeStruct` for serializing to XML.
 pub struct Struct<'r, 'w, W>
 where
@@ -185,6 +192,8 @@ where
     }
 }
 
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
 /// An implementation of `SerializeSeq' for serializing to XML.
 pub struct Seq<'r, 'w, W>
 where
@@ -223,6 +232,8 @@ where
     }
 }
 
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
 /// An implementation of `SerializeTuple`, `SerializeTupleStruct` and
 /// `SerializeTupleVariant` for serializing to XML.
 pub struct Tuple<'r, 'w, W>
@@ -308,3 +319,24 @@ where
         <Self as ser::SerializeTuple>::end(self)
     }
 }
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+#[test]
+fn test_serialize_map_entries() {
+    use serde::ser::SerializeMap;
+
+    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();
+    }
+
+    assert_eq!(
+        String::from_utf8(buffer).unwrap(),
+        "<name>Bob</name><age>5</age>"
+    );
+}
diff --git a/src/utils.rs b/src/utils.rs
index 130532a..185cff4 100644
--- a/src/utils.rs
+++ b/src/utils.rs
@@ -3,6 +3,8 @@ use std::fmt::{self, Debug, Formatter};
 
 #[cfg(feature = "serialize")]
 use serde::de::{Deserialize, Deserializer, Error, Visitor};
+#[cfg(feature = "serialize")]
+use serde::ser::{Serialize, Serializer};
 
 pub fn write_cow_string(f: &mut Formatter, cow_string: &Cow<[u8]>) -> fmt::Result {
     match cow_string {
@@ -76,6 +78,16 @@ impl<'de> Deserialize<'de> for ByteBuf {
     }
 }
 
+#[cfg(feature = "serialize")]
+impl Serialize for ByteBuf {
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: Serializer,
+    {
+        serializer.serialize_bytes(&self.0)
+    }
+}
+
 ////////////////////////////////////////////////////////////////////////////////////////////////////
 
 /// Wrapper around `&[u8]` that has a human-readable debug representation:
@@ -117,6 +129,16 @@ impl<'de> Deserialize<'de> for Bytes<'de> {
     }
 }
 
+#[cfg(feature = "serialize")]
+impl<'de> Serialize for Bytes<'de> {
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: Serializer,
+    {
+        serializer.serialize_bytes(self.0)
+    }
+}
+
 ////////////////////////////////////////////////////////////////////////////////////////////////////
 
 #[cfg(test)]


Test patch:
diff --git a/tests/serde-se.rs b/tests/serde-se.rs
new file mode 100644
index 0000000..99d2b5b
--- /dev/null
+++ b/tests/serde-se.rs
@@ -0,0 +1,493 @@
+use quick_xml::se::{to_string, Serializer};
+use quick_xml::writer::Writer;
+
+use pretty_assertions::assert_eq;
+
+use serde::{Serialize, Serializer as SerSerializer};
+
+#[test]
+fn serialize_bool() {
+    let inputs = [(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();
+
+        assert_eq!(String::from_utf8(buffer).unwrap(), *should_be);
+    }
+}
+
+#[test]
+fn serialize_struct() {
+    #[derive(Serialize)]
+    struct Person {
+        name: String,
+        age: u32,
+    }
+
+    let bob = Person {
+        name: "Bob".to_string(),
+        age: 42,
+    };
+
+    let mut buffer = Vec::new();
+    let mut ser = Serializer::new(&mut buffer);
+    bob.serialize(&mut ser).unwrap();
+
+    assert_eq!(
+        String::from_utf8(buffer).unwrap(),
+        "<Person name=\"Bob\" age=\"42\"/>"
+    );
+}
+
+#[test]
+fn serialize_struct_value_number() {
+    #[derive(Serialize)]
+    struct Person {
+        name: String,
+        #[serde(rename = "$value")]
+        age: u32,
+    }
+
+    let bob = Person {
+        name: "Bob".to_string(),
+        age: 42,
+    };
+    assert_eq!(to_string(&bob).unwrap(), "<Person name=\"Bob\">42</Person>");
+}
+
+#[test]
+fn serialize_struct_value_string() {
+    #[derive(Serialize)]
+    struct Person {
+        name: String,
+        #[serde(rename = "$value")]
+        age: String,
+    }
+
+    let bob = Person {
+        name: "Bob".to_string(),
+        age: "42".to_string(),
+    };
+    assert_eq!(to_string(&bob).unwrap(), "<Person name=\"Bob\">42</Person>");
+}
+
+#[test]
+fn serialize_enum() {
+    #[derive(Serialize)]
+    #[allow(dead_code)]
+    enum Node {
+        Boolean(bool),
+        Number(f64),
+        String(String),
+    }
+
+    let mut buffer = Vec::new();
+    let mut ser = Serializer::new(&mut buffer);
+    let node = Node::Boolean(true);
+    node.serialize(&mut ser).unwrap();
+
+    assert_eq!(
+        String::from_utf8(buffer).unwrap(),
+        "<Boolean>true</Boolean>"
+    );
+}
+
+#[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();
+
+    println!("{}", String::from_utf8(buffer).unwrap());
+    panic!();
+}
+
+#[derive(Serialize)]
+struct Unit;
+
+#[derive(Serialize)]
+struct Newtype(bool);
+
+#[derive(Serialize)]
+struct Tuple(f32, &'static str);
+
+#[derive(Serialize)]
+struct Struct {
+    float: f64,
+    string: &'static str,
+}
+
+#[derive(Serialize)]
+struct NestedStruct {
+    nested: Nested,
+    string: &'static str,
+}
+
+#[derive(Serialize)]
+struct FlattenStruct {
+    #[serde(flatten)]
+    nested: Nested,
+    string: &'static str,
+}
+
+#[derive(Serialize)]
+struct Nested {
+    float: f64,
+}
+
+#[derive(Serialize)]
+struct Empty {}
+
+#[derive(Serialize)]
+struct Value {
+    #[serde(rename = "$value")]
+    float: f64,
+    string: &'static str,
+}
+
+#[derive(Serialize)]
+enum ExternallyTagged {
+    Unit,
+    #[serde(rename = "$primitive=PrimitiveUnit")]
+    PrimitiveUnit,
+    Newtype(bool),
+    Tuple(f64, &'static str),
+    Struct {
+        float: f64,
+        string: &'static str,
+    },
+    Holder {
+        nested: Nested,
+        string: &'static str,
+    },
+    Flatten {
+        #[serde(flatten)]
+        nested: Nested,
+        string: &'static str,
+    },
+    Empty {},
+    Value {
+        #[serde(rename = "$value")]
+        float: f64,
+        string: &'static str,
+    },
+}
+
+#[derive(Serialize)]
+#[serde(tag = "tag")]
+enum InternallyTagged {
+    Unit,
+    /// Primitives (such as `bool`) are not supported by the serde in the internally tagged mode
+    Newtype(Nested),
+    // Tuple(f64, &'static str),// Tuples are not supported in the internally tagged mode
+    Struct {
+        float: f64,
+        string: &'static str,
+    },
+    Holder {
+        nested: Nested,
+        string: &'static str,
+    },
+    Flatten {
+        #[serde(flatten)]
+        nested: Nested,
+        string: &'static str,
+    },
+    Empty {},
+    Value {
+        #[serde(rename = "$value")]
+        float: f64,
+        string: &'static str,
+    },
+}
+
+#[derive(Serialize)]
+#[serde(tag = "tag", content = "content")]
+enum AdjacentlyTagged {
+    Unit,
+    Newtype(bool),
+    Tuple(f64, &'static str),
+    Struct {
+        float: f64,
+        string: &'static str,
+    },
+    Holder {
+        nested: Nested,
+        string: &'static str,
+    },
+    Flatten {
+        #[serde(flatten)]
+        nested: Nested,
+        string: &'static str,
+    },
+    Empty {},
+    Value {
+        #[serde(rename = "$value")]
+        float: f64,
+        string: &'static str,
+    },
+}
+
+#[derive(Serialize)]
+#[serde(untagged)]
+enum Untagged {
+    Unit,
+    Newtype(bool),
+    Tuple(f64, &'static str),
+    Struct {
+        float: f64,
+        string: &'static str,
+    },
+    Holder {
+        nested: Nested,
+        string: &'static str,
+    },
+    Flatten {
+        #[serde(flatten)]
+        nested: Nested,
+        string: &'static str,
+    },
+    Empty {},
+    Value {
+        #[serde(rename = "$value")]
+        float: f64,
+        string: &'static str,
+    },
+}
+
+mod with_root {
+    use super::*;
+    use pretty_assertions::assert_eq;
+
+    macro_rules! serialize_as {
+        ($name:ident: $data:expr => $expected:literal) => {
+            #[test]
+            fn $name() {
+                let mut buffer = Vec::new();
+                let mut ser = Serializer::with_root(Writer::new(&mut buffer), Some("root"));
+
+                $data.serialize(&mut ser).unwrap();
+                assert_eq!(String::from_utf8(buffer).unwrap(), $expected);
+            }
+        };
+    }
+
+    serialize_as!(unit:
+        Unit
+        => "<root/>");
+    serialize_as!(newtype:
+        Newtype(true)
+        => "<root>true</root>");
+    serialize_as!(tuple:
+        (42.0, "answer")
+        => "<root>42</root><root>answer</root>");
+    serialize_as!(tuple_struct:
+        Tuple(42.0, "answer")
+        => "<root>42</root><root>answer</root>");
+    serialize_as!(struct_:
+        Struct {
+            float: 42.0,
+            string: "answer"
+        }
+        => r#"<root float="42" string="answer"/>"#);
+    serialize_as!(nested_struct:
+        NestedStruct {
+            nested: Nested { float: 42.0 },
+            string: "answer",
+        }
+        => r#"<root string="answer"><nested float="42"/></root>"#);
+    serialize_as!(flatten_struct:
+        FlattenStruct {
+            nested: Nested { float: 42.0 },
+            string: "answer",
+        }
+        => r#"<root><float>42</float><string>answer</string></root>"#);
+    serialize_as!(empty_struct:
+        Empty {}
+        => "<root/>");
+    serialize_as!(value:
+        Value {
+            float: 42.0,
+            string: "answer"
+        }
+        => r#"<root string="answer">42</root>"#);
+
+    mod enum_ {
+        use super::*;
+
+        mod externally_tagged {
+            use super::*;
+            use pretty_assertions::assert_eq;
+
+            serialize_as!(unit:
+                ExternallyTagged::Unit
+                => "<Unit/>");
+            serialize_as!(primitive_unit:
+                ExternallyTagged::PrimitiveUnit
+                => "PrimitiveUnit");
+            serialize_as!(newtype:
+                ExternallyTagged::Newtype(true)
+                => "<Newtype>true</Newtype>");
+            serialize_as!(tuple_struct:
+                ExternallyTagged::Tuple(42.0, "answer")
+                => "<Tuple>42</Tuple><Tuple>answer</Tuple>");
+            serialize_as!(struct_:
+                ExternallyTagged::Struct {
+                    float: 42.0,
+                    string: "answer",
+                }
+                => r#"<Struct float="42" string="answer"/>"#);
+            serialize_as!(nested_struct:
+                ExternallyTagged::Holder {
+                    nested: Nested { float: 42.0 },
+                    string: "answer",
+                }
+                => r#"<Holder string="answer"><nested float="42"/></Holder>"#);
+            serialize_as!(flatten_struct:
+                ExternallyTagged::Flatten {
+                    nested: Nested { float: 42.0 },
+                    string: "answer",
+                }
+                => r#"<Flatten><float>42</float><string>answer</string></Flatten>"#);
+            serialize_as!(empty_struct:
+                ExternallyTagged::Empty {}
+                => "<Empty/>");
+            serialize_as!(value:
+                ExternallyTagged::Value {
+                    float: 42.0,
+                    string: "answer"
+                }
+                => r#"<Value string="answer">42</Value>"#);
+        }
+
+        mod internally_tagged {
+            use super::*;
+            use pretty_assertions::assert_eq;
+
+            serialize_as!(unit:
+                InternallyTagged::Unit
+                => r#"<root tag="Unit"/>"#);
+            serialize_as!(newtype:
+                InternallyTagged::Newtype(Nested { float: 4.2 })
+                => r#"<root tag="Newtype" float="4.2"/>"#);
+            serialize_as!(struct_:
+                InternallyTagged::Struct {
+                    float: 42.0,
+                    string: "answer",
+                }
+                => r#"<root tag="Struct" float="42" string="answer"/>"#);
+            serialize_as!(nested_struct:
+                InternallyTagged::Holder {
+                    nested: Nested { float: 42.0 },
+                    string: "answer",
+                }
+                => r#"<root tag="Holder" string="answer"><nested float="42"/></root>"#);
+            serialize_as!(flatten_struct:
+                InternallyTagged::Flatten {
+                    nested: Nested { float: 42.0 },
+                    string: "answer",
+                }
+                => r#"<root><tag>Flatten</tag><float>42</float><string>answer</string></root>"#);
+            serialize_as!(empty_struct:
+                InternallyTagged::Empty {}
+                => r#"<root tag="Empty"/>"#);
+            serialize_as!(value:
+                InternallyTagged::Value {
+                    float: 42.0,
+                    string: "answer"
+                }
+                => r#"<root tag="Value" string="answer">42</root>"#);
+        }
+
+        mod adjacently_tagged {
+            use super::*;
+            use pretty_assertions::assert_eq;
+
+            serialize_as!(unit:
+                AdjacentlyTagged::Unit
+                => r#"<root tag="Unit"/>"#);
+            serialize_as!(newtype:
+                AdjacentlyTagged::Newtype(true)
+                => r#"<root tag="Newtype" content="true"/>"#);
+            serialize_as!(tuple_struct:
+                AdjacentlyTagged::Tuple(42.0, "answer")
+                => r#"<root tag="Tuple"><content>42</content><content>answer</content></root>"#);
+            serialize_as!(struct_:
+                AdjacentlyTagged::Struct {
+                    float: 42.0,
+                    string: "answer",
+                }
+                => r#"<root tag="Struct"><content float="42" string="answer"/></root>"#);
+            serialize_as!(nested_struct:
+                AdjacentlyTagged::Holder {
+                    nested: Nested { float: 42.0 },
+                    string: "answer",
+                }
+                => r#"<root tag="Holder"><content string="answer"><nested float="42"/></content></root>"#);
+            serialize_as!(flatten_struct:
+                AdjacentlyTagged::Flatten {
+                    nested: Nested { float: 42.0 },
+                    string: "answer",
+                }
+                => r#"<root tag="Flatten"><content><float>42</float><string>answer</string></content></root>"#);
+            serialize_as!(empty_struct:
+                AdjacentlyTagged::Empty {}
+                => r#"<root tag="Empty"><content/></root>"#);
+            serialize_as!(value:
+                AdjacentlyTagged::Value {
+                    float: 42.0,
+                    string: "answer",
+                }
+                => r#"<root tag="Value"><content string="answer">42</content></root>"#);
+        }
+
+        mod untagged {
+            use super::*;
+            use pretty_assertions::assert_eq;
+
+            serialize_as!(unit:
+                Untagged::Unit
+                // Unit variant consists just from the tag, and because tags
+                // are not written in untagged mode, nothing is written
+                => "");
+            serialize_as!(newtype:
+                Untagged::Newtype(true)
+                => "true");
+            serialize_as!(tuple_struct:
+                Untagged::Tuple(42.0, "answer")
+                => "<root>42</root><root>answer</root>");
+            serialize_as!(struct_:
+                Untagged::Struct {
+                    float: 42.0,
+                    string: "answer",
+                }
+                => r#"<root float="42" string="answer"/>"#);
+            serialize_as!(nested_struct:
+                Untagged::Holder {
+                    nested: Nested { float: 42.0 },
+                    string: "answer",
+                }
+                => r#"<root string="answer"><nested float="42"/></root>"#);
+            serialize_as!(flatten_struct:
+                Untagged::Flatten {
+                    nested: Nested { float: 42.0 },
+                    string: "answer",
+                }
+                => r#"<root><float>42</float><string>answer</string></root>"#);
+            serialize_as!(empty_struct:
+                Untagged::Empty {}
+                => "<root/>");
+            serialize_as!(value:
+                Untagged::Value {
+                    float: 42.0,
+                    string: "answer"
+                }
+                => r#"<root string="answer">42</root>"#);
+        }
+    }
+}