diff --git a/Cargo.toml b/Cargo.toml
index 1f871430..73a9a9f8 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -16,8 +16,7 @@ include = ["src/*", "LICENSE-MIT.md", "README.md"]
[dependencies]
document-features = { version = "0.2", optional = true }
encoding_rs = { version = "0.8", optional = true }
-# FIXME: remove upper bound when https://github.com/tafia/quick-xml/issues/630 is resolved
-serde = { version = ">=1.0.100,<1.0.181", optional = true }
+serde = { version = ">=1.0.100", optional = true }
tokio = { version = "1.10", optional = true, default-features = false, features = ["io-util"] }
memchr = "2.1"
arbitrary = { version = "1", features = ["derive"], optional = true }
diff --git a/Changelog.md b/Changelog.md
index aa9ecb9b..8de5fa4e 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -12,6 +12,9 @@
MSRV bumped to 1.56! Crate now uses Rust 2021 edition.
+Enum representation was changed (it was buggy anyway) to ensure compatibility with
+serde >= 1.0.181
+
### New Features
- [#545]: Resolve well-known namespaces (`xml` and `xmlns`) to their appropriate URIs.
@@ -40,11 +43,13 @@ MSRV bumped to 1.56! Crate now uses Rust 2021 edition.
(and newly added `ElementWriter::write_inner_content_async` of course).
- [#662]: Get rid of some allocations during serde deserialization.
- [#665]: Improve serialization of `xs:list`s when some elements serialized to an empty string.
+- [#630]: Fixed compatibility with serde >= 1.0.181
[#545]: https://github.com/tafia/quick-xml/pull/545
[#567]: https://github.com/tafia/quick-xml/issues/567
[#580]: https://github.com/tafia/quick-xml/issues/580
[#619]: https://github.com/tafia/quick-xml/issues/619
+[#630]: https://github.com/tafia/quick-xml/issues/630
[#635]: https://github.com/tafia/quick-xml/pull/635
[#643]: https://github.com/tafia/quick-xml/pull/643
[#649]: https://github.com/tafia/quick-xml/pull/646
diff --git a/src/de/map.rs b/src/de/map.rs
index 7abd45df..97e632da 100644
--- a/src/de/map.rs
+++ b/src/de/map.rs
@@ -613,7 +613,27 @@ where
where
V: Visitor<'de>,
{
- visitor.visit_enum(self)
+ if self.fixed_name {
+ match self.map.de.next()? {
+ // Handles UnitEnumVariant
+ DeEvent::Start(_) => {
+ // skip , read text after it and ensure that it is ended by
+ let text = self.map.de.read_text()?;
+ if text.is_empty() {
+ // Map empty text () to a special `$text` variant
+ visitor.visit_enum(SimpleTypeDeserializer::from_text(TEXT_KEY.into()))
+ } else {
+ visitor.visit_enum(SimpleTypeDeserializer::from_text(text))
+ }
+ }
+ // SAFETY: we use that deserializer with `fixed_name == true`
+ // only from the `MapAccess::next_value_seed` and only when we
+ // peeked `Start` event
+ _ => unreachable!(),
+ }
+ } else {
+ visitor.visit_enum(self)
+ }
}
fn deserialize_any(self, visitor: V) -> Result
diff --git a/src/de/mod.rs b/src/de/mod.rs
index 248d82c2..a7190f6e 100644
--- a/src/de/mod.rs
+++ b/src/de/mod.rs
@@ -20,6 +20,9 @@
//! - [Choices (`xs:choice` XML Schema type)](#choices-xschoice-xml-schema-type)
//! - [Sequences (`xs:all` and `xs:sequence` XML Schema types)](#sequences-xsall-and-xssequence-xml-schema-types)
//! - [Composition Rules](#composition-rules)
+//! - [Enum Representations](#enum-representations)
+//! - [Normal enum variant](#normal-enum-variant)
+//! - [`$text` enum variant](#text-enum-variant)
//! - [Difference between `$text` and `$value` special names](#difference-between-text-and-value-special-names)
//! - [`$text`](#text)
//! - [`$value`](#value)
@@ -29,7 +32,6 @@
//! - [Frequently Used Patterns](#frequently-used-patterns)
//! - [`` lists](#element-lists)
//! - [Overlapped (Out-of-Order) Elements](#overlapped-out-of-order-elements)
-//! - [Enum::Unit Variants As a Text](#enumunit-variants-as-a-text)
//! - [Internally Tagged Enums](#internally-tagged-enums)
//!
//!
@@ -1351,6 +1353,58 @@
//!
//!
//!
+//! Enum Representations
+//! ====================
+//!
+//! `quick-xml` represents enums differently in normal fields, `$text` fields and
+//! `$value` fields. A normal representation is compatible with serde's adjacent
+//! and internal tags feature -- tag for adjacently and internally tagged enums
+//! are serialized using [`Serializer::serialize_unit_variant`] and deserialized
+//! using [`Deserializer::deserialize_enum`].
+//!
+//! Use those simple rules to remember, how enum would be represented in XML:
+//! - In `$value` field the representation is always the same as top-level representation;
+//! - In `$text` field the representation is always the same as in normal field,
+//! but surrounding tags with field name are removed;
+//! - In normal field the representation is always contains a tag with a field name.
+//!
+//! Normal enum variant
+//! -------------------
+//!
+//! To model an `xs:choice` XML construct use `$value` field.
+//! To model a top-level `xs:choice` just use the enum type.
+//!
+//! |Kind |Top-level and in `$value` field |In normal field |In `$text` field |
+//! |-------|-----------------------------------------|---------------------|---------------------|
+//! |Unit |`` |`Unit`|`Unit` |
+//! |Newtype|`42` |Err(Unsupported) |Err(Unsupported) |
+//! |Tuple |`42answer` |Err(Unsupported) |Err(Unsupported) |
+//! |Struct |`42
answer`|Err(Unsupported) |Err(Unsupported) |
+//!
+//! `$text` enum variant
+//! --------------------
+//!
+//! |Kind |Top-level and in `$value` field |In normal field |In `$text` field |
+//! |-------|-----------------------------------------|---------------------|---------------------|
+//! |Unit |_(empty)_ |`` |_(empty)_ |
+//! |Newtype|`42` |Err(Unsupported) [^1]|Err(Unsupported) [^2]|
+//! |Tuple |`42 answer` |Err(Unsupported) [^3]|Err(Unsupported) [^4]|
+//! |Struct |Err(Unsupported) |Err(Unsupported) |Err(Unsupported) |
+//!
+//! [^1]: If this serialize as `42` then it will be ambiguity during deserialization,
+//! because it clash with `Unit` representation in normal field.
+//!
+//! [^2]: If this serialize as `42` then it will be ambiguity during deserialization,
+//! because it clash with `Unit` representation in `$text` field.
+//!
+//! [^3]: If this serialize as `42 answer` then it will be ambiguity during deserialization,
+//! because it clash with `Unit` representation in normal field.
+//!
+//! [^4]: If this serialize as `42 answer` then it will be ambiguity during deserialization,
+//! because it clash with `Unit` representation in `$text` field.
+//!
+//!
+//!
//! Difference between `$text` and `$value` special names
//! =====================================================
//!
@@ -1431,33 +1485,54 @@
//! get their names from the field name. It cannot be deserialized, because `Enum`
//! expects elements ``, `` or ``, but `AnyName` looked only for ``:
//!
-//! ```no_run
+//! ```
//! # use serde::{Deserialize, Serialize};
+//! # use pretty_assertions::assert_eq;
+//! # #[derive(PartialEq, Debug)]
//! #[derive(Deserialize, Serialize)]
//! enum Enum { A, B, C }
//!
+//! # #[derive(PartialEq, Debug)]
//! #[derive(Deserialize, Serialize)]
//! struct AnyName {
-//! //
+//! // A, B, or C
//! field: Enum,
//! }
+//! # assert_eq!(
+//! # quick_xml::se::to_string(&AnyName { field: Enum::A }).unwrap(),
+//! # "A",
+//! # );
+//! # assert_eq!(
+//! # AnyName { field: Enum::B },
+//! # quick_xml::de::from_str("B").unwrap(),
+//! # );
//! ```
//!
//! If you rename field to `$value`, then `field` would be serialized as ``,
//! `` or ``, depending on the its content. It is also possible to
//! deserialize it from the same elements:
//!
-//! ```no_run
+//! ```
//! # use serde::{Deserialize, Serialize};
-//! # #[derive(Deserialize, Serialize)]
+//! # use pretty_assertions::assert_eq;
+//! # #[derive(Deserialize, Serialize, PartialEq, Debug)]
//! # enum Enum { A, B, C }
//! #
+//! # #[derive(PartialEq, Debug)]
//! #[derive(Deserialize, Serialize)]
//! struct AnyName {
//! // , or
//! #[serde(rename = "$value")]
//! field: Enum,
//! }
+//! # assert_eq!(
+//! # quick_xml::se::to_string(&AnyName { field: Enum::A }).unwrap(),
+//! # "",
+//! # );
+//! # assert_eq!(
+//! # AnyName { field: Enum::B },
+//! # quick_xml::de::from_str("").unwrap(),
+//! # );
//! ```
//!
//! ### Primitives and sequences of primitives
@@ -1467,6 +1542,7 @@
//!
//! ```
//! # use serde::{Deserialize, Serialize};
+//! # use pretty_assertions::assert_eq;
//! # use quick_xml::de::from_str;
//! # use quick_xml::se::to_string;
//! #[derive(Deserialize, Serialize, PartialEq, Debug)]
@@ -1493,6 +1569,7 @@
//!
//! ```
//! # use serde::{Deserialize, Serialize};
+//! # use pretty_assertions::assert_eq;
//! # use quick_xml::de::from_str;
//! # use quick_xml::se::to_string;
//! #[derive(Deserialize, Serialize, PartialEq, Debug)]
@@ -1516,6 +1593,7 @@
//!
//! ```
//! # use serde::{Deserialize, Serialize};
+//! # use pretty_assertions::assert_eq;
//! # use quick_xml::de::from_str;
//! # use quick_xml::se::to_string;
//! #[derive(Deserialize, Serialize, PartialEq, Debug)]
@@ -1549,6 +1627,7 @@
//!
//! ```
//! # use serde::{Deserialize, Serialize};
+//! # use pretty_assertions::assert_eq;
//! # use quick_xml::de::from_str;
//! # use quick_xml::se::to_string;
//! #[derive(Deserialize, Serialize, PartialEq, Debug)]
@@ -1708,75 +1787,6 @@
//! }
//! ```
//!
-//! Enum::Unit Variants As a Text
-//! -----------------------------
-//! One frequent task and a typical mistake is to creation of mapping a text
-//! content of some tag to a Rust `enum`. For example, for the XML:
-//!
-//! ```xml
-//!
-//! EnumValue
-//!
-//! ```
-//! one could create an _incorrect_ mapping
-//!
-//! ```
-//! # use serde::{Deserialize, Serialize};
-//! #
-//! #[derive(Serialize, Deserialize)]
-//! enum SomeEnum {
-//! EnumValue,
-//! # /*
-//! ...
-//! # */
-//! }
-//!
-//! #[derive(Serialize, Deserialize)]
-//! #[serde(rename = "some-container")]
-//! struct SomeContainer {
-//! field: SomeEnum,
-//! }
-//! ```
-//!
-//! Actually, those types will be serialized into:
-//! ```xml
-//!
-//!
-//!
-//! ```
-//! and will not be able to be deserialized.
-//!
-//! You can easily see what's wrong if you think about attributes, which could
-//! be defined in the `` tag:
-//! ```xml
-//!
-//! EnumValue
-//!
-//! ```
-//!
-//! After that you can find the correct solution, using the principles explained
-//! above. You should wrap `SomeEnum` into wrapper struct under the [`$text`](#text)
-//! name:
-//! ```
-//! # use serde::{Serialize, Deserialize};
-//! # type SomeEnum = ();
-//! #[derive(Serialize, Deserialize)]
-//! struct Field {
-//! // Use a special name `$text` to map field to the text content
-//! #[serde(rename = "$text")]
-//! content: SomeEnum,
-//! }
-//!
-//! #[derive(Serialize, Deserialize)]
-//! #[serde(rename = "some-container")]
-//! struct SomeContainer {
-//! field: Field,
-//! }
-//! ```
-//!
-//! If you still want to keep your struct untouched, you can instead use the
-//! helper module [`text_content`].
-//!
//!
//! Internally Tagged Enums
//! -----------------------
@@ -1794,7 +1804,8 @@
//! [specification]: https://www.w3.org/TR/xmlschema11-1/#Simple_Type_Definition
//! [`deserialize_with`]: https://serde.rs/field-attrs.html#deserialize_with
//! [#497]: https://github.com/tafia/quick-xml/issues/497
-//! [`text_content`]: crate::serde_helpers::text_content
+//! [`Serializer::serialize_unit_variant`]: serde::Serializer::serialize_unit_variant
+//! [`Deserializer::deserialize_enum`]: serde::Deserializer::deserialize_enum
//! [Tagged enums]: https://serde.rs/enum-representations.html#internally-tagged
//! [serde#1183]: https://github.com/serde-rs/serde/issues/1183
//! [serde#1495]: https://github.com/serde-rs/serde/issues/1495
diff --git a/src/se/content.rs b/src/se/content.rs
index 376f55ac..984689f0 100644
--- a/src/se/content.rs
+++ b/src/se/content.rs
@@ -1,5 +1,6 @@
//! Contains serializer for content of an XML element
+use crate::de::TEXT_KEY;
use crate::errors::serialize::DeError;
use crate::se::element::{ElementSerializer, Struct, Tuple};
use crate::se::simple_type::{QuoteTarget, SimpleTypeSerializer};
@@ -22,19 +23,32 @@ macro_rules! write_primitive {
////////////////////////////////////////////////////////////////////////////////////////////////////
-/// A serializer used to serialize content of the element. It does not write
-/// surrounding tags.
+/// A serializer used to serialize content of an element. It does not write
+/// surrounding tags. Unlike the [`ElementSerializer`], this serializer serializes
+/// enums using variant names as tag names, i. e. as `...`
///
/// This serializer does the following:
-/// - primitives (booleans, numbers and strings) serialized as naked strings
-/// - `None` does not write anything
-/// - sequences serialized without delimiters. `[1, 2, 3]` would be serialized as `123`
-/// - units (`()`) and unit structs are not supported
-/// - structs and maps are not supported
-/// - unit variants serialized as self-closed `<${variant}/>`
-/// - tuple variants serialized as sequences where each is wrapped in
-/// `<${variant}>...${variant}>`
-/// - struct variants serialized wrapped `<${variant}>...${variant}>`
+/// - numbers converted to a decimal representation and serialized as naked strings;
+/// - booleans serialized ether as `"true"` or `"false"`;
+/// - strings and characters are serialized as naked strings;
+/// - `None` does not write anything;
+/// - `Some` and newtypes are serialized as an inner type using the same serializer;
+/// - units (`()`) and unit structs does not write anything;
+/// - sequences, tuples and tuple structs are serialized without delimiters.
+/// `[1, 2, 3]` would be serialized as `123` (if not using indent);
+/// - structs and maps are not supported ([`DeError::Unsupported`] is returned);
+/// - enums:
+/// - unit variants are serialized as self-closed ``;
+/// - newtype variants are serialized as inner value wrapped in `...`;
+/// - tuple variants are serialized as sequences where each element is wrapped
+/// in `...`;
+/// - struct variants are serialized as a sequence of fields wrapped in
+/// `...`. Each field is serialized recursively using
+/// either [`ElementSerializer`], `ContentSerializer` (`$value` fields), or
+/// [`SimpleTypeSerializer`] (`$text` fields). In particular, the empty struct
+/// is serialized as ``;
+///
+/// Usage of empty tags depends on the [`Self::expand_empty_elements`] setting.
///
/// The difference between this serializer and [`SimpleTypeSerializer`] is in how
/// sequences and maps are serialized. Unlike `SimpleTypeSerializer` it supports
@@ -202,15 +216,20 @@ impl<'w, 'i, W: Write> Serializer for ContentSerializer<'w, 'i, W> {
Ok(())
}
- /// Checks `variant` for XML name validity and writes `<${variant}/>`
+ /// If `variant` is a special `$text` variant, then do nothing, otherwise
+ /// checks `variant` for XML name validity and writes ``.
fn serialize_unit_variant(
self,
_name: &'static str,
_variant_index: u32,
variant: &'static str,
) -> Result {
- let name = XmlName::try_from(variant)?;
- self.write_empty(name)
+ if variant == TEXT_KEY {
+ Ok(())
+ } else {
+ let name = XmlName::try_from(variant)?;
+ self.write_empty(name)
+ }
}
fn serialize_newtype_struct(
@@ -221,8 +240,9 @@ impl<'w, 'i, W: Write> Serializer for ContentSerializer<'w, 'i, W> {
value.serialize(self)
}
- /// Checks `variant` for XML name validity and writes `value` as new element
- /// with name `variant`.
+ /// If `variant` is a special `$text` variant, then writes `value` as a `xs:simpleType`,
+ /// otherwise checks `variant` for XML name validity and writes `value` as a new
+ /// `` element.
fn serialize_newtype_variant(
self,
_name: &'static str,
@@ -230,10 +250,15 @@ impl<'w, 'i, W: Write> Serializer for ContentSerializer<'w, 'i, W> {
variant: &'static str,
value: &T,
) -> Result {
- value.serialize(ElementSerializer {
- key: XmlName::try_from(variant)?,
- ser: self,
- })
+ if variant == TEXT_KEY {
+ value.serialize(self.into_simple_type_serializer())?;
+ Ok(())
+ } else {
+ value.serialize(ElementSerializer {
+ key: XmlName::try_from(variant)?,
+ ser: self,
+ })
+ }
}
#[inline]
@@ -255,6 +280,14 @@ impl<'w, 'i, W: Write> Serializer for ContentSerializer<'w, 'i, W> {
self.serialize_tuple(len)
}
+ /// Serializes variant as a tuple with name `variant`, producing
+ ///
+ /// ```xml
+ ///
+ ///
+ ///
+ ///
+ /// ```
#[inline]
fn serialize_tuple_variant(
self,
@@ -263,14 +296,17 @@ impl<'w, 'i, W: Write> Serializer for ContentSerializer<'w, 'i, W> {
variant: &'static str,
len: usize,
) -> Result {
- let ser = ElementSerializer {
- key: XmlName::try_from(variant)?,
- ser: self,
- };
- // `ElementSerializer::serialize_tuple_variant` is the same as
- // `ElementSerializer::serialize_tuple_struct`, except that it replaces `.key`
- // to `variant` which is not required here
- ser.serialize_tuple_struct(name, len).map(Tuple::Element)
+ if variant == TEXT_KEY {
+ self.into_simple_type_serializer()
+ .serialize_tuple_struct(name, len)
+ .map(Tuple::Text)
+ } else {
+ let ser = ElementSerializer {
+ key: XmlName::try_from(variant)?,
+ ser: self,
+ };
+ ser.serialize_tuple_struct(name, len).map(Tuple::Element)
+ }
}
fn serialize_map(self, _len: Option) -> Result {
@@ -290,6 +326,16 @@ impl<'w, 'i, W: Write> Serializer for ContentSerializer<'w, 'i, W> {
))
}
+ /// Serializes variant as an element with name `variant`, producing
+ ///
+ /// ```xml
+ ///
+ ///
+ ///
+ /// ```
+ ///
+ /// If struct has no fields which is represented by nested elements or a text,
+ /// it may be serialized as self-closed element ``.
#[inline]
fn serialize_struct_variant(
self,
@@ -298,14 +344,17 @@ impl<'w, 'i, W: Write> Serializer for ContentSerializer<'w, 'i, W> {
variant: &'static str,
len: usize,
) -> Result {
- let ser = ElementSerializer {
- key: XmlName::try_from(variant)?,
- ser: self,
- };
- // `ElementSerializer::serialize_struct_variant` is the same as
- // `ElementSerializer::serialize_struct`, except that it replaces `.key`
- // to `variant` which is not required here
- ser.serialize_struct(name, len)
+ if variant == TEXT_KEY {
+ Err(DeError::Unsupported(
+ format!("cannot serialize `$text` struct variant of `{}` enum", name).into(),
+ ))
+ } else {
+ let ser = ElementSerializer {
+ key: XmlName::try_from(variant)?,
+ ser: self,
+ };
+ ser.serialize_struct(name, len)
+ }
}
}
@@ -394,6 +443,7 @@ pub(super) mod tests {
pub val: (usize, usize),
}
+ /// Struct with a special `$text` field
#[derive(Debug, Serialize, PartialEq)]
pub struct Text {
pub before: &'static str,
@@ -402,6 +452,7 @@ pub(super) mod tests {
pub after: &'static str,
}
+ /// Struct with a special `$value` field
#[derive(Debug, Serialize, PartialEq)]
pub struct Value {
pub before: &'static str,
@@ -465,12 +516,14 @@ pub(super) mod tests {
#[derive(Debug, Serialize, PartialEq)]
pub enum SpecialEnum {
+ /// Struct variant with a special `$text` field
Text {
before: &'static str,
#[serde(rename = "$text")]
content: T,
after: &'static str,
},
+ /// Struct variant with a special `$value` field
Value {
before: &'static str,
#[serde(rename = "$value")]
@@ -486,7 +539,7 @@ pub(super) mod tests {
/// Checks that given `$data` successfully serialized as `$expected`
macro_rules! serialize_as {
- ($name:ident: $data:expr => $expected:literal) => {
+ ($name:ident: $data:expr => $expected:expr) => {
#[test]
fn $name() {
let mut buffer = String::new();
@@ -618,7 +671,7 @@ pub(super) mod tests {
");
/// Special field name `$text` should be serialized as a text content
- mod text {
+ mod text_field {
use super::*;
use pretty_assertions::assert_eq;
@@ -644,6 +697,255 @@ pub(super) mod tests {
");
}
+ /// `$text` field inside a struct variant of an enum
+ mod enum_with_text_field {
+ use super::*;
+ use pretty_assertions::assert_eq;
+
+ macro_rules! text {
+ ($name:ident: $data:expr => $expected:literal) => {
+ serialize_as!($name:
+ SpecialEnum::Text {
+ before: "answer",
+ content: $data,
+ after: "answer",
+ }
+ => concat!(
+ "answer",
+ $expected,
+ "answer",
+ ));
+ };
+ }
+
+ text!(false_: false => "false");
+ text!(true_: true => "true");
+
+ text!(i8_: -42i8 => "-42");
+ text!(i16_: -4200i16 => "-4200");
+ text!(i32_: -42000000i32 => "-42000000");
+ text!(i64_: -42000000000000i64 => "-42000000000000");
+ text!(isize_: -42000000000000isize => "-42000000000000");
+
+ text!(u8_: 42u8 => "42");
+ text!(u16_: 4200u16 => "4200");
+ text!(u32_: 42000000u32 => "42000000");
+ text!(u64_: 42000000000000u64 => "42000000000000");
+ text!(usize_: 42000000000000usize => "42000000000000");
+
+ serde_if_integer128! {
+ text!(i128_: -420000000000000000000000000000i128 => "-420000000000000000000000000000");
+ text!(u128_: 420000000000000000000000000000u128 => "420000000000000000000000000000");
+ }
+
+ text!(f32_: 4.2f32 => "4.2");
+ text!(f64_: 4.2f64 => "4.2");
+
+ text!(char_non_escaped: 'h' => "h");
+ text!(char_lt: '<' => "<");
+ text!(char_gt: '>' => ">");
+ text!(char_amp: '&' => "&");
+ text!(char_apos: '\'' => "'");
+ text!(char_quot: '"' => """);
+ //TODO: add a setting to escape leading/trailing spaces, in order to
+ // pretty-print does not change the content
+ text!(char_space: ' ' => " ");
+
+ text!(str_non_escaped: "non-escaped string" => "non-escaped string");
+ text!(str_escaped: "<\"escaped & string'>" => "<"escaped & string'>");
+
+ err!(bytes:
+ SpecialEnum::Text {
+ before: "answer",
+ content: Bytes(b"<\"escaped & bytes'>"),
+ after: "answer",
+ }
+ => Unsupported("`serialize_bytes` not supported yet"));
+
+ text!(option_none: Option::<&str>::None => "");
+ text!(option_some: Some("non-escaped string") => "non-escaped string");
+ text!(option_some_empty_str: Some("") => "");
+
+ text!(unit: () => "");
+ text!(unit_struct: Unit => "");
+ text!(unit_struct_escaped: UnitEscaped => "");
+
+ text!(enum_unit: Enum::Unit => "Unit");
+ text!(enum_unit_escaped: Enum::UnitEscaped => "<"&'>");
+
+ text!(newtype: Newtype(42) => "42");
+ // We have no space where name of a variant can be stored
+ err!(enum_newtype:
+ SpecialEnum::Text {
+ before: "answer",
+ content: Enum::Newtype(42),
+ after: "answer",
+ }
+ => Unsupported("cannot serialize enum newtype variant `Enum::Newtype` as text content value"));
+
+ // Sequences are serialized separated by spaces, all spaces inside are escaped
+ text!(seq: vec![1, 2, 3] => "1 2 3");
+ text!(seq_empty: Vec::::new() => "");
+ text!(tuple: ("<\"&'>", "with\t\n\r spaces", 3usize)
+ => "<"&'> \
+ with
spaces \
+ 3");
+ text!(tuple_struct: Tuple("first", 42) => "first 42");
+ // We have no space where name of a variant can be stored
+ err!(enum_tuple:
+ SpecialEnum::Text {
+ before: "answer",
+ content: Enum::Tuple("first", 42),
+ after: "answer",
+ }
+ => Unsupported("cannot serialize enum tuple variant `Enum::Tuple` as text content value"));
+
+ // Complex types cannot be serialized in `$text` field
+ err!(map:
+ SpecialEnum::Text {
+ before: "answer",
+ content: BTreeMap::from([("_1", 2), ("_3", 4)]),
+ after: "answer",
+ }
+ => Unsupported("cannot serialize map as text content value"));
+ err!(struct_:
+ SpecialEnum::Text {
+ before: "answer",
+ content: Struct { key: "answer", val: (42, 42) },
+ after: "answer",
+ }
+ => Unsupported("cannot serialize struct `Struct` as text content value"));
+ err!(enum_struct:
+ SpecialEnum::Text {
+ before: "answer",
+ content: Enum::Struct { key: "answer", val: (42, 42) },
+ after: "answer",
+ }
+ => Unsupported("cannot serialize enum struct variant `Enum::Struct` as text content value"));
+ }
+
+ /// `$value` field inside a struct variant of an enum
+ mod enum_with_value_field {
+ use super::*;
+ use pretty_assertions::assert_eq;
+
+ macro_rules! value {
+ ($name:ident: $data:expr => $expected:literal) => {
+ serialize_as!($name:
+ SpecialEnum::Value {
+ before: "answer",
+ content: $data,
+ after: "answer",
+ }
+ => concat!(
+ "answer",
+ $expected,
+ "answer",
+ ));
+ };
+ }
+
+ value!(false_: false => "false");
+ value!(true_: true => "true");
+
+ value!(i8_: -42i8 => "-42");
+ value!(i16_: -4200i16 => "-4200");
+ value!(i32_: -42000000i32 => "-42000000");
+ value!(i64_: -42000000000000i64 => "-42000000000000");
+ value!(isize_: -42000000000000isize => "-42000000000000");
+
+ value!(u8_: 42u8 => "42");
+ value!(u16_: 4200u16 => "4200");
+ value!(u32_: 42000000u32 => "42000000");
+ value!(u64_: 42000000000000u64 => "42000000000000");
+ value!(usize_: 42000000000000usize => "42000000000000");
+
+ serde_if_integer128! {
+ value!(i128_: -420000000000000000000000000000i128 => "-420000000000000000000000000000");
+ value!(u128_: 420000000000000000000000000000u128 => "420000000000000000000000000000");
+ }
+
+ value!(f32_: 4.2f32 => "4.2");
+ value!(f64_: 4.2f64 => "4.2");
+
+ value!(char_non_escaped: 'h' => "h");
+ value!(char_lt: '<' => "<");
+ value!(char_gt: '>' => ">");
+ value!(char_amp: '&' => "&");
+ value!(char_apos: '\'' => "'");
+ value!(char_quot: '"' => """);
+ //TODO: add a setting to escape leading/trailing spaces, in order to
+ // pretty-print does not change the content
+ value!(char_space: ' ' => " ");
+
+ value!(str_non_escaped: "non-escaped string" => "non-escaped string");
+ value!(str_escaped: "<\"escaped & string'>" => "<"escaped & string'>");
+
+ err!(bytes:
+ SpecialEnum::Value {
+ before: "answer",
+ content: Bytes(b"<\"escaped & bytes'>"),
+ after: "answer",
+ }
+ => Unsupported("`serialize_bytes` not supported yet"));
+
+ value!(option_none: Option::<&str>::None => "");
+ value!(option_some: Some("non-escaped string") => "non-escaped string");
+ value!(option_some_empty_str: Some("") => "");
+
+ value!(unit: () => "");
+ value!(unit_struct: Unit => "");
+ value!(unit_struct_escaped: UnitEscaped => "");
+
+ value!(enum_unit: Enum::Unit => "");
+ err!(enum_unit_escaped:
+ SpecialEnum::Value {
+ before: "answer",
+ content: Enum::UnitEscaped,
+ after: "answer",
+ }
+ => Unsupported("character `<` is not allowed at the start of an XML name `<\"&'>`"));
+
+ value!(newtype: Newtype(42) => "42");
+ value!(enum_newtype: Enum::Newtype(42) => "42");
+
+ // Note that sequences of primitives serialized without delimiters!
+ value!(seq: vec![1, 2, 3] => "123");
+ value!(seq_empty: Vec::::new() => "");
+ value!(tuple: ("<\"&'>", "with\t\n\r spaces", 3usize)
+ => "<"&'>\
+ with\t\n\r spaces\
+ 3");
+ value!(tuple_struct: Tuple("first", 42) => "first42");
+ value!(enum_tuple: Enum::Tuple("first", 42)
+ => "first\
+ 42");
+
+ // We cannot wrap map or struct in any container and should not
+ // flatten it, so it is impossible to serialize maps and structs
+ err!(map:
+ SpecialEnum::Value {
+ before: "answer",
+ content: BTreeMap::from([("_1", 2), ("_3", 4)]),
+ after: "answer",
+ }
+ => Unsupported("serialization of map types is not supported in `$value` field"));
+ err!(struct_:
+ SpecialEnum::Value {
+ before: "answer",
+ content: Struct { key: "answer", val: (42, 42) },
+ after: "answer",
+ }
+ => Unsupported("serialization of struct `Struct` is not supported in `$value` field"));
+ value!(enum_struct:
+ Enum::Struct { key: "answer", val: (42, 42) }
+ => "\
+ answer\
+ 42\
+ 42\
+ ");
+ }
+
mod attributes {
use super::*;
use pretty_assertions::assert_eq;
@@ -677,7 +979,7 @@ pub(super) mod tests {
/// Checks that given `$data` successfully serialized as `$expected`
macro_rules! serialize_as {
- ($name:ident: $data:expr => $expected:literal) => {
+ ($name:ident: $data:expr => $expected:expr) => {
#[test]
fn $name() {
let mut buffer = String::new();
@@ -810,7 +1112,7 @@ pub(super) mod tests {
");
/// Special field name `$text` should be serialized as text content
- mod text {
+ mod text_field {
use super::*;
use pretty_assertions::assert_eq;
@@ -836,6 +1138,279 @@ pub(super) mod tests {
");
}
+ /// `$text` field inside a struct variant of an enum
+ mod enum_with_text_field {
+ use super::*;
+ use pretty_assertions::assert_eq;
+
+ macro_rules! text {
+ ($name:ident: $data:expr) => {
+ serialize_as!($name:
+ SpecialEnum::Text {
+ before: "answer",
+ content: $data,
+ after: "answer",
+ }
+ => "\n \
+ answer\n \
+ answer\n\
+ ");
+ };
+ ($name:ident: $data:expr => $expected:literal) => {
+ serialize_as!($name:
+ SpecialEnum::Text {
+ before: "answer",
+ content: $data,
+ after: "answer",
+ }
+ => concat!(
+ "\n answer\n ",
+ $expected,
+ "\n answer\n",
+ ));
+ };
+ }
+
+ text!(false_: false => "false");
+ text!(true_: true => "true");
+
+ text!(i8_: -42i8 => "-42");
+ text!(i16_: -4200i16 => "-4200");
+ text!(i32_: -42000000i32 => "-42000000");
+ text!(i64_: -42000000000000i64 => "-42000000000000");
+ text!(isize_: -42000000000000isize => "-42000000000000");
+
+ text!(u8_: 42u8 => "42");
+ text!(u16_: 4200u16 => "4200");
+ text!(u32_: 42000000u32 => "42000000");
+ text!(u64_: 42000000000000u64 => "42000000000000");
+ text!(usize_: 42000000000000usize => "42000000000000");
+
+ serde_if_integer128! {
+ text!(i128_: -420000000000000000000000000000i128 => "-420000000000000000000000000000");
+ text!(u128_: 420000000000000000000000000000u128 => "420000000000000000000000000000");
+ }
+
+ text!(f32_: 4.2f32 => "4.2");
+ text!(f64_: 4.2f64 => "4.2");
+
+ text!(char_non_escaped: 'h' => "h");
+ text!(char_lt: '<' => "<");
+ text!(char_gt: '>' => ">");
+ text!(char_amp: '&' => "&");
+ text!(char_apos: '\'' => "'");
+ text!(char_quot: '"' => """);
+ //TODO: add a setting to escape leading/trailing spaces, in order to
+ // pretty-print does not change the content
+ text!(char_space: ' ' => " ");
+
+ text!(str_non_escaped: "non-escaped string" => "non-escaped string");
+ text!(str_escaped: "<\"escaped & string'>" => "<"escaped & string'>");
+
+ err!(bytes:
+ SpecialEnum::Text {
+ before: "answer",
+ content: Bytes(b"<\"escaped & bytes'>"),
+ after: "answer",
+ }
+ => Unsupported("`serialize_bytes` not supported yet"));
+
+ text!(option_none: Option::<&str>::None);
+ text!(option_some: Some("non-escaped string") => "non-escaped string");
+ text!(option_some_empty_str: Some(""));
+
+ text!(unit: ());
+ text!(unit_struct: Unit);
+ text!(unit_struct_escaped: UnitEscaped);
+
+ text!(enum_unit: Enum::Unit => "Unit");
+ text!(enum_unit_escaped: Enum::UnitEscaped => "<"&'>");
+
+ text!(newtype: Newtype(42) => "42");
+ // We have no space where name of a variant can be stored
+ err!(enum_newtype:
+ SpecialEnum::Text {
+ before: "answer",
+ content: Enum::Newtype(42),
+ after: "answer",
+ }
+ => Unsupported("cannot serialize enum newtype variant `Enum::Newtype` as text content value"));
+
+ // Sequences are serialized separated by spaces, all spaces inside are escaped
+ text!(seq: vec![1, 2, 3] => "1 2 3");
+ text!(seq_empty: Vec::::new());
+ text!(tuple: ("<\"&'>", "with\t\n\r spaces", 3usize)
+ => "<"&'> \
+ with
spaces \
+ 3");
+ text!(tuple_struct: Tuple("first", 42) => "first 42");
+ // We have no space where name of a variant can be stored
+ err!(enum_tuple:
+ SpecialEnum::Text {
+ before: "answer",
+ content: Enum::Tuple("first", 42),
+ after: "answer",
+ }
+ => Unsupported("cannot serialize enum tuple variant `Enum::Tuple` as text content value"));
+
+ // Complex types cannot be serialized in `$text` field
+ err!(map:
+ SpecialEnum::Text {
+ before: "answer",
+ content: BTreeMap::from([("_1", 2), ("_3", 4)]),
+ after: "answer",
+ }
+ => Unsupported("cannot serialize map as text content value"));
+ err!(struct_:
+ SpecialEnum::Text {
+ before: "answer",
+ content: Struct { key: "answer", val: (42, 42) },
+ after: "answer",
+ }
+ => Unsupported("cannot serialize struct `Struct` as text content value"));
+ err!(enum_struct:
+ SpecialEnum::Text {
+ before: "answer",
+ content: Enum::Struct { key: "answer", val: (42, 42) },
+ after: "answer",
+ }
+ => Unsupported("cannot serialize enum struct variant `Enum::Struct` as text content value"));
+ }
+
+ /// `$value` field inside a struct variant of an enum
+ mod enum_with_value_field {
+ use super::*;
+ use pretty_assertions::assert_eq;
+
+ macro_rules! value {
+ ($name:ident: $data:expr) => {
+ serialize_as!($name:
+ SpecialEnum::Value {
+ before: "answer",
+ content: $data,
+ after: "answer",
+ }
+ => "\n \
+ answer\n \
+ answer\n\
+ ");
+ };
+ ($name:ident: $data:expr => $expected:literal) => {
+ serialize_as!($name:
+ SpecialEnum::Value {
+ before: "answer",
+ content: $data,
+ after: "answer",
+ }
+ => concat!(
+ "\n answer\n ",
+ $expected,
+ "\n answer\n",
+ ));
+ };
+ }
+
+ value!(false_: false => "false");
+ value!(true_: true => "true");
+
+ value!(i8_: -42i8 => "-42");
+ value!(i16_: -4200i16 => "-4200");
+ value!(i32_: -42000000i32 => "-42000000");
+ value!(i64_: -42000000000000i64 => "-42000000000000");
+ value!(isize_: -42000000000000isize => "-42000000000000");
+
+ value!(u8_: 42u8 => "42");
+ value!(u16_: 4200u16 => "4200");
+ value!(u32_: 42000000u32 => "42000000");
+ value!(u64_: 42000000000000u64 => "42000000000000");
+ value!(usize_: 42000000000000usize => "42000000000000");
+
+ serde_if_integer128! {
+ value!(i128_: -420000000000000000000000000000i128 => "-420000000000000000000000000000");
+ value!(u128_: 420000000000000000000000000000u128 => "420000000000000000000000000000");
+ }
+
+ value!(f32_: 4.2f32 => "4.2");
+ value!(f64_: 4.2f64 => "4.2");
+
+ value!(char_non_escaped: 'h' => "h");
+ value!(char_lt: '<' => "<");
+ value!(char_gt: '>' => ">");
+ value!(char_amp: '&' => "&");
+ value!(char_apos: '\'' => "'");
+ value!(char_quot: '"' => """);
+ //TODO: add a setting to escape leading/trailing spaces, in order to
+ // pretty-print does not change the content
+ value!(char_space: ' ' => " ");
+
+ value!(str_non_escaped: "non-escaped string" => "non-escaped string");
+ value!(str_escaped: "<\"escaped & string'>" => "<"escaped & string'>");
+
+ err!(bytes:
+ SpecialEnum::Value {
+ before: "answer",
+ content: Bytes(b"<\"escaped & bytes'>"),
+ after: "answer",
+ }
+ => Unsupported("`serialize_bytes` not supported yet"));
+
+ value!(option_none: Option::<&str>::None);
+ value!(option_some: Some("non-escaped string") => "non-escaped string");
+ value!(option_some_empty_str: Some(""));
+
+ value!(unit: ());
+ value!(unit_struct: Unit);
+ value!(unit_struct_escaped: UnitEscaped);
+
+ value!(enum_unit: Enum::Unit => "");
+ err!(enum_unit_escaped:
+ SpecialEnum::Value {
+ before: "answer",
+ content: Enum::UnitEscaped,
+ after: "answer",
+ }
+ => Unsupported("character `<` is not allowed at the start of an XML name `<\"&'>`"));
+
+ value!(newtype: Newtype(42) => "42");
+ value!(enum_newtype: Enum::Newtype(42) => "42");
+
+ // Note that sequences of primitives serialized without delimiters!
+ value!(seq: vec![1, 2, 3] => "1\n 2\n 3");
+ value!(seq_empty: Vec::::new());
+ value!(tuple: ("<\"&'>", "with\t\n\r spaces", 3usize)
+ => "<"&'>\n \
+ with\t\n\r spaces\n \
+ 3");
+ value!(tuple_struct: Tuple("first", 42) => "first\n 42");
+ value!(enum_tuple: Enum::Tuple("first", 42)
+ => "first\n \
+ 42");
+
+ // We cannot wrap map or struct in any container and should not
+ // flatten it, so it is impossible to serialize maps and structs
+ err!(map:
+ SpecialEnum::Value {
+ before: "answer",
+ content: BTreeMap::from([("_1", 2), ("_3", 4)]),
+ after: "answer",
+ }
+ => Unsupported("serialization of map types is not supported in `$value` field"));
+ err!(struct_:
+ SpecialEnum::Value {
+ before: "answer",
+ content: Struct { key: "answer", val: (42, 42) },
+ after: "answer",
+ }
+ => Unsupported("serialization of struct `Struct` is not supported in `$value` field"));
+ value!(enum_struct:
+ Enum::Struct { key: "answer", val: (42, 42) }
+ => "\n \
+ answer\n \
+ 42\n \
+ 42\n \
+ ");
+ }
+
mod attributes {
use super::*;
use pretty_assertions::assert_eq;
diff --git a/src/se/element.rs b/src/se/element.rs
index f5a3e187..afad2a97 100644
--- a/src/se/element.rs
+++ b/src/se/element.rs
@@ -5,14 +5,16 @@ use crate::errors::serialize::DeError;
use crate::se::content::ContentSerializer;
use crate::se::key::QNameSerializer;
use crate::se::simple_type::{QuoteTarget, SimpleSeq, SimpleTypeSerializer};
+use crate::se::text::TextSerializer;
use crate::se::{Indent, XmlName};
use serde::ser::{
- Serialize, SerializeMap, SerializeSeq, SerializeStruct, SerializeStructVariant, SerializeTuple,
- SerializeTupleStruct, SerializeTupleVariant, Serializer,
+ Impossible, Serialize, SerializeMap, SerializeSeq, SerializeStruct, SerializeStructVariant,
+ SerializeTuple, SerializeTupleStruct, SerializeTupleVariant, Serializer,
};
use serde::serde_if_integer128;
use std::fmt::Write;
+/// Writes simple type content between [`ElementSerializer::key`] tags.
macro_rules! write_primitive {
($method:ident ( $ty:ty )) => {
fn $method(self, value: $ty) -> Result {
@@ -23,8 +25,39 @@ macro_rules! write_primitive {
////////////////////////////////////////////////////////////////////////////////////////////////////
-/// A serializer used to serialize element with specified name.
+/// A serializer used to serialize element with specified name. Unlike the [`ContentSerializer`],
+/// this serializer never uses variant names of enum variants, and because of that
+/// it is unable to serialize any enum values, except unit variants.
+///
+/// This serializer is used for an ordinary fields in structs, which are not special
+/// fields named `$text` ([`TEXT_KEY`]) or `$value` ([`VALUE_KEY`]). `$text` field
+/// should be serialized using [`SimpleTypeSerializer`] and `$value` field should be
+/// serialized using [`ContentSerializer`].
+///
+/// This serializer does the following:
+/// - numbers converted to a decimal representation and serialized as `value`;
+/// - booleans serialized ether as `true` or `false`;
+/// - strings and characters are serialized as `value`. In particular,
+/// an empty string is serialized as ``;
+/// - `None` is serialized as ``;
+/// - `Some` and newtypes are serialized as an inner type using the same serializer;
+/// - units (`()`) and unit structs are serialized as ``;
+/// - sequences, tuples and tuple structs are serialized as repeated `` tag.
+/// In particular, empty sequence is serialized to nothing;
+/// - structs are serialized as a sequence of fields wrapped in a `` tag. Each
+/// field is serialized recursively using either `ElementSerializer`, [`ContentSerializer`]
+/// (`$value` fields), or [`SimpleTypeSerializer`] (`$text` fields).
+/// In particular, the empty struct is serialized as ``;
+/// - maps are serialized as a sequence of entries wrapped in a `` tag. If key is
+/// serialized to a special name, the same rules as for struct fields are applied.
+/// In particular, the empty map is serialized as ``;
+/// - enums:
+/// - unit variants are serialized as `variant`;
+/// - other variants are not supported ([`DeError::Unsupported`] is returned);
+///
+/// Usage of empty tags depends on the [`ContentSerializer::expand_empty_elements`] setting.
pub struct ElementSerializer<'w, 'k, W: Write> {
+ /// The inner serializer that contains the settings and mostly do the actual work
pub ser: ContentSerializer<'w, 'k, W>,
/// Tag name used to wrap serialized types except enum variants which uses the variant name
pub(super) key: XmlName<'k>,
@@ -37,7 +70,7 @@ impl<'w, 'k, W: Write> Serializer for ElementSerializer<'w, 'k, W> {
type SerializeSeq = Self;
type SerializeTuple = Self;
type SerializeTupleStruct = Self;
- type SerializeTupleVariant = Tuple<'w, 'k, W>;
+ type SerializeTupleVariant = Impossible;
type SerializeMap = Map<'w, 'k, W>;
type SerializeStruct = Struct<'w, 'k, W>;
type SerializeStructVariant = Struct<'w, 'k, W>;
@@ -103,24 +136,21 @@ impl<'w, 'k, W: Write> Serializer for ElementSerializer<'w, 'k, W> {
self.ser.write_empty(self.key)
}
+ /// Writes a tag with name [`Self::key`] and content of unit variant inside.
+ /// If variant is a special `$text` value, then empty tag `` is written.
+ /// Otherwise a `variant` is written.
fn serialize_unit_variant(
self,
name: &'static str,
- _variant_index: u32,
+ variant_index: u32,
variant: &'static str,
) -> Result {
if variant == TEXT_KEY {
- // We should write some text but we don't known what text to write
- Err(DeError::Unsupported(
- format!(
- "cannot serialize enum unit variant `{}::$text` as text content value",
- name
- )
- .into(),
- ))
+ self.ser.write_empty(self.key)
} else {
- let name = XmlName::try_from(variant)?;
- self.ser.write_empty(name)
+ self.ser.write_wrapped(self.key, |ser| {
+ ser.serialize_unit_variant(name, variant_index, variant)
+ })
}
}
@@ -132,20 +162,23 @@ impl<'w, 'k, W: Write> Serializer for ElementSerializer<'w, 'k, W> {
value.serialize(self)
}
+ /// Always returns [`DeError::Unsupported`]. Newtype variants can be serialized
+ /// only in `$value` fields, which is serialized using [`ContentSerializer`].
+ #[inline]
fn serialize_newtype_variant(
- mut self,
- _name: &'static str,
+ self,
+ name: &'static str,
_variant_index: u32,
variant: &'static str,
- value: &T,
+ _value: &T,
) -> Result {
- if variant == TEXT_KEY {
- value.serialize(self.ser.into_simple_type_serializer())?;
- Ok(())
- } else {
- self.key = XmlName::try_from(variant)?;
- value.serialize(self)
- }
+ Err(DeError::Unsupported(
+ format!(
+ "cannot serialize enum newtype variant `{}::{}`",
+ name, variant
+ )
+ .into(),
+ ))
}
#[inline]
@@ -167,23 +200,23 @@ impl<'w, 'k, W: Write> Serializer for ElementSerializer<'w, 'k, W> {
self.serialize_tuple(len)
}
+ /// Always returns [`DeError::Unsupported`]. Tuple variants can be serialized
+ /// only in `$value` fields, which is serialized using [`ContentSerializer`].
#[inline]
fn serialize_tuple_variant(
- mut self,
+ self,
name: &'static str,
_variant_index: u32,
variant: &'static str,
- len: usize,
+ _len: usize,
) -> Result {
- if variant == TEXT_KEY {
- self.ser
- .into_simple_type_serializer()
- .serialize_tuple_struct(name, len)
- .map(Tuple::Text)
- } else {
- self.key = XmlName::try_from(variant)?;
- self.serialize_tuple_struct(name, len).map(Tuple::Element)
- }
+ Err(DeError::Unsupported(
+ format!(
+ "cannot serialize enum tuple variant `{}::{}`",
+ name, variant
+ )
+ .into(),
+ ))
}
fn serialize_map(self, _len: Option) -> Result {
@@ -210,26 +243,23 @@ impl<'w, 'k, W: Write> Serializer for ElementSerializer<'w, 'k, W> {
})
}
+ /// Always returns [`DeError::Unsupported`]. Struct variants can be serialized
+ /// only in `$value` fields, which is serialized using [`ContentSerializer`].
#[inline]
fn serialize_struct_variant(
- mut self,
+ self,
name: &'static str,
_variant_index: u32,
variant: &'static str,
- len: usize,
+ _len: usize,
) -> Result {
- if variant == TEXT_KEY {
- Err(DeError::Unsupported(
- format!(
- "cannot serialize enum struct variant `{}::$text` as text content value",
- name
- )
- .into(),
- ))
- } else {
- self.key = XmlName::try_from(variant)?;
- self.serialize_struct(name, len)
- }
+ Err(DeError::Unsupported(
+ format!(
+ "cannot serialize enum struct variant `{}::{}`",
+ name, variant
+ )
+ .into(),
+ ))
}
}
@@ -245,7 +275,7 @@ impl<'w, 'k, W: Write> SerializeSeq for ElementSerializer<'w, 'k, W> {
ser: self.ser.new_seq_element_serializer(),
key: self.key,
})?;
- // Write indent for next element
+ // Write indent for the next element
self.ser.write_indent = true;
Ok(())
}
@@ -409,7 +439,7 @@ impl<'w, 'k, W: Write> Struct<'w, 'k, W> {
};
if key == TEXT_KEY {
- value.serialize(ser.into_simple_type_serializer())?;
+ value.serialize(TextSerializer(ser.into_simple_type_serializer()))?;
} else if key == VALUE_KEY {
value.serialize(ser)?;
} else {
@@ -670,12 +700,12 @@ mod tests {
serialize_as!(unit_struct: Unit => "");
serialize_as!(unit_struct_escaped: UnitEscaped => "");
- serialize_as!(enum_unit: Enum::Unit => "");
- err!(enum_unit_escaped: Enum::UnitEscaped
- => Unsupported("character `<` is not allowed at the start of an XML name `<\"&'>`"));
+ serialize_as!(enum_unit: Enum::Unit => "Unit");
+ serialize_as!(enum_unit_escaped: Enum::UnitEscaped => "<"&'>");
serialize_as!(newtype: Newtype(42) => "42");
- serialize_as!(enum_newtype: Enum::Newtype(42) => "42");
+ err!(enum_newtype: Enum::Newtype(42)
+ => Unsupported("cannot serialize enum newtype variant `Enum::Newtype`"));
serialize_as!(seq: vec![1, 2, 3]
=> "1\
@@ -689,9 +719,8 @@ mod tests {
serialize_as!(tuple_struct: Tuple("first", 42)
=> "first\
42");
- serialize_as!(enum_tuple: Enum::Tuple("first", 42)
- => "first\
- 42");
+ err!(enum_tuple: Enum::Tuple("first", 42)
+ => Unsupported("cannot serialize enum tuple variant `Enum::Tuple`"));
serialize_as!(map: BTreeMap::from([("_1", 2), ("_3", 4)])
=> "\
@@ -704,16 +733,12 @@ mod tests {
42\
42\
");
- serialize_as!(enum_struct: Enum::Struct { key: "answer", val: (42, 42) }
- => "\
- answer\
- 42\
- 42\
- ");
+ err!(enum_struct: Enum::Struct { key: "answer", val: (42, 42) }
+ => Unsupported("cannot serialize enum struct variant `Enum::Struct`"));
/// Special field name `$text` should be serialized as text content.
/// Sequences serialized as an `xs:list` content
- mod text {
+ mod text_field {
use super::*;
/// `$text` key in a map
@@ -797,7 +822,7 @@ mod tests {
content: Enum::Newtype(42),
after: "answer",
}
- => Unsupported("cannot serialize enum newtype variant `Enum::Newtype` as an attribute or text content value"));
+ => Unsupported("cannot serialize enum newtype variant `Enum::Newtype` as text content value"));
// Sequences are serialized separated by spaces, all spaces inside are escaped
text!(seq: vec![1, 2, 3] => "1 2 3");
@@ -814,7 +839,7 @@ mod tests {
content: Enum::Tuple("first", 42),
after: "answer",
}
- => Unsupported("cannot serialize enum tuple variant `Enum::Tuple` as an attribute or text content value"));
+ => Unsupported("cannot serialize enum tuple variant `Enum::Tuple` as text content value"));
// Complex types cannot be serialized in `$text` field
err!(map:
@@ -823,21 +848,21 @@ mod tests {
content: BTreeMap::from([("_1", 2), ("_3", 4)]),
after: "answer",
}
- => Unsupported("cannot serialize map as an attribute or text content value"));
+ => Unsupported("cannot serialize map as text content value"));
err!(struct_:
Text {
before: "answer",
content: Struct { key: "answer", val: (42, 42) },
after: "answer",
}
- => Unsupported("cannot serialize struct `Struct` as an attribute or text content value"));
+ => Unsupported("cannot serialize struct `Struct` as text content value"));
err!(enum_struct:
Text {
before: "answer",
content: Enum::Struct { key: "answer", val: (42, 42) },
after: "answer",
}
- => Unsupported("cannot serialize enum struct variant `Enum::Struct` as an attribute or text content value"));
+ => Unsupported("cannot serialize enum struct variant `Enum::Struct` as text content value"));
}
/// `$text` field inside a struct
@@ -924,7 +949,7 @@ mod tests {
content: Enum::Newtype(42),
after: "answer",
}
- => Unsupported("cannot serialize enum newtype variant `Enum::Newtype` as an attribute or text content value"));
+ => Unsupported("cannot serialize enum newtype variant `Enum::Newtype` as text content value"));
// Sequences are serialized separated by spaces, all spaces inside are escaped
text!(seq: vec![1, 2, 3] => "1 2 3");
@@ -941,7 +966,7 @@ mod tests {
content: Enum::Tuple("first", 42),
after: "answer",
}
- => Unsupported("cannot serialize enum tuple variant `Enum::Tuple` as an attribute or text content value"));
+ => Unsupported("cannot serialize enum tuple variant `Enum::Tuple` as text content value"));
// Complex types cannot be serialized in `$text` field
err!(map:
@@ -950,155 +975,28 @@ mod tests {
content: BTreeMap::from([("_1", 2), ("_3", 4)]),
after: "answer",
}
- => Unsupported("cannot serialize map as an attribute or text content value"));
+ => Unsupported("cannot serialize map as text content value"));
err!(struct_:
Text {
before: "answer",
content: Struct { key: "answer", val: (42, 42) },
after: "answer",
}
- => Unsupported("cannot serialize struct `Struct` as an attribute or text content value"));
+ => Unsupported("cannot serialize struct `Struct` as text content value"));
err!(enum_struct:
Text {
before: "answer",
content: Enum::Struct { key: "answer", val: (42, 42) },
after: "answer",
}
- => Unsupported("cannot serialize enum struct variant `Enum::Struct` as an attribute or text content value"));
- }
-
- /// `$text` field inside a struct variant of an enum
- mod enum_struct {
- use super::*;
- use pretty_assertions::assert_eq;
-
- macro_rules! text {
- ($name:ident: $data:expr => $expected:literal) => {
- serialize_as!($name:
- SpecialEnum::Text {
- before: "answer",
- content: $data,
- after: "answer",
- }
- => concat!(
- "answer",
- $expected,
- "answer",
- ));
- };
- }
-
- text!(false_: false => "false");
- text!(true_: true => "true");
-
- text!(i8_: -42i8 => "-42");
- text!(i16_: -4200i16 => "-4200");
- text!(i32_: -42000000i32 => "-42000000");
- text!(i64_: -42000000000000i64 => "-42000000000000");
- text!(isize_: -42000000000000isize => "-42000000000000");
-
- text!(u8_: 42u8 => "42");
- text!(u16_: 4200u16 => "4200");
- text!(u32_: 42000000u32 => "42000000");
- text!(u64_: 42000000000000u64 => "42000000000000");
- text!(usize_: 42000000000000usize => "42000000000000");
-
- serde_if_integer128! {
- text!(i128_: -420000000000000000000000000000i128 => "-420000000000000000000000000000");
- text!(u128_: 420000000000000000000000000000u128 => "420000000000000000000000000000");
- }
-
- text!(f32_: 4.2f32 => "4.2");
- text!(f64_: 4.2f64 => "4.2");
-
- text!(char_non_escaped: 'h' => "h");
- text!(char_lt: '<' => "<");
- text!(char_gt: '>' => ">");
- text!(char_amp: '&' => "&");
- text!(char_apos: '\'' => "'");
- text!(char_quot: '"' => """);
- //TODO: add a setting to escape leading/trailing spaces, in order to
- // pretty-print does not change the content
- text!(char_space: ' ' => " ");
-
- text!(str_non_escaped: "non-escaped string" => "non-escaped string");
- text!(str_escaped: "<\"escaped & string'>" => "<"escaped & string'>");
-
- err!(bytes:
- SpecialEnum::Text {
- before: "answer",
- content: Bytes(b"<\"escaped & bytes'>"),
- after: "answer",
- }
- => Unsupported("`serialize_bytes` not supported yet"));
-
- text!(option_none: Option::<&str>::None => "");
- text!(option_some: Some("non-escaped string") => "non-escaped string");
- text!(option_some_empty_str: Some("") => "");
-
- text!(unit: () => "");
- text!(unit_struct: Unit => "");
- text!(unit_struct_escaped: UnitEscaped => "");
-
- text!(enum_unit: Enum::Unit => "Unit");
- text!(enum_unit_escaped: Enum::UnitEscaped => "<"&'>");
-
- text!(newtype: Newtype(42) => "42");
- // We have no space where name of a variant can be stored
- err!(enum_newtype:
- SpecialEnum::Text {
- before: "answer",
- content: Enum::Newtype(42),
- after: "answer",
- }
- => Unsupported("cannot serialize enum newtype variant `Enum::Newtype` as an attribute or text content value"));
-
- // Sequences are serialized separated by spaces, all spaces inside are escaped
- text!(seq: vec![1, 2, 3] => "1 2 3");
- text!(seq_empty: Vec::::new() => "");
- text!(tuple: ("<\"&'>", "with\t\n\r spaces", 3usize)
- => "<"&'> \
- with
spaces \
- 3");
- text!(tuple_struct: Tuple("first", 42) => "first 42");
- // We have no space where name of a variant can be stored
- err!(enum_tuple:
- SpecialEnum::Text {
- before: "answer",
- content: Enum::Tuple("first", 42),
- after: "answer",
- }
- => Unsupported("cannot serialize enum tuple variant `Enum::Tuple` as an attribute or text content value"));
-
- // Complex types cannot be serialized in `$text` field
- err!(map:
- SpecialEnum::Text {
- before: "answer",
- content: BTreeMap::from([("_1", 2), ("_3", 4)]),
- after: "answer",
- }
- => Unsupported("cannot serialize map as an attribute or text content value"));
- err!(struct_:
- SpecialEnum::Text {
- before: "answer",
- content: Struct { key: "answer", val: (42, 42) },
- after: "answer",
- }
- => Unsupported("cannot serialize struct `Struct` as an attribute or text content value"));
- err!(enum_struct:
- SpecialEnum::Text {
- before: "answer",
- content: Enum::Struct { key: "answer", val: (42, 42) },
- after: "answer",
- }
- => Unsupported("cannot serialize enum struct variant `Enum::Struct` as an attribute or text content value"));
+ => Unsupported("cannot serialize enum struct variant `Enum::Struct` as text content value"));
}
}
/// Special field name `$value` should be serialized using name, provided
/// by the type of value instead of a key. Sequences serialized as a list
/// of tags with that name (each element can have their own name)
- mod value {
+ mod value_field {
use super::*;
/// `$value` key in a map
@@ -1325,180 +1223,54 @@ mod tests {
42\
");
}
+ }
- /// `$value` field inside a struct variant of an enum
- mod enum_struct {
- use super::*;
- use pretty_assertions::assert_eq;
-
- macro_rules! value {
- ($name:ident: $data:expr => $expected:literal) => {
- serialize_as!($name:
- SpecialEnum::Value {
- before: "answer",
- content: $data,
- after: "answer",
- }
- => concat!(
- "answer",
- $expected,
- "answer",
- ));
- };
- }
-
- value!(false_: false => "false");
- value!(true_: true => "true");
-
- value!(i8_: -42i8 => "-42");
- value!(i16_: -4200i16 => "-4200");
- value!(i32_: -42000000i32 => "-42000000");
- value!(i64_: -42000000000000i64 => "-42000000000000");
- value!(isize_: -42000000000000isize => "-42000000000000");
-
- value!(u8_: 42u8 => "42");
- value!(u16_: 4200u16 => "4200");
- value!(u32_: 42000000u32 => "42000000");
- value!(u64_: 42000000000000u64 => "42000000000000");
- value!(usize_: 42000000000000usize => "42000000000000");
+ mod attributes {
+ use super::*;
+ use pretty_assertions::assert_eq;
- serde_if_integer128! {
- value!(i128_: -420000000000000000000000000000i128 => "-420000000000000000000000000000");
- value!(u128_: 420000000000000000000000000000u128 => "420000000000000000000000000000");
- }
+ serialize_as!(map_attr: BTreeMap::from([("@key1", 1), ("@key2", 2)])
+ => r#""#);
+ serialize_as!(map_mixed: BTreeMap::from([("@key1", 1), ("key2", 2)])
+ => r#"2"#);
- value!(f32_: 4.2f32 => "4.2");
- value!(f64_: 4.2f64 => "4.2");
+ serialize_as!(struct_: Attributes { key: "answer", val: (42, 42) }
+ => r#""#);
+ serialize_as!(struct_before: AttributesBefore { key: "answer", val: 42 }
+ => r#"42"#);
+ serialize_as!(struct_after: AttributesAfter { key: "answer", val: 42 }
+ => r#"answer"#);
- value!(char_non_escaped: 'h' => "h");
- value!(char_lt: '<' => "<");
- value!(char_gt: '>' => ">");
- value!(char_amp: '&' => "&");
- value!(char_apos: '\'' => "'");
- value!(char_quot: '"' => """);
- //TODO: add a setting to escape leading/trailing spaces, in order to
- // pretty-print does not change the content
- value!(char_space: ' ' => " ");
+ err!(enum_: Enum::Attributes { key: "answer", val: (42, 42) }
+ => Unsupported("cannot serialize enum struct variant `Enum::Attributes`"));
- value!(str_non_escaped: "non-escaped string" => "non-escaped string");
- value!(str_escaped: "<\"escaped & string'>" => "<"escaped & string'>");
+ /// Test for https://github.com/tafia/quick-xml/issues/252
+ mod optional {
+ use super::*;
+ use pretty_assertions::assert_eq;
- err!(bytes:
- SpecialEnum::Value {
- before: "answer",
- content: Bytes(b"<\"escaped & bytes'>"),
- after: "answer",
+ serialize_as!(none:
+ OptionalAttributes { a: None, b: None }
+ => r#""#);
+ serialize_as!(some_empty_str:
+ OptionalAttributes {
+ a: Some(""),
+ b: Some(""),
}
- => Unsupported("`serialize_bytes` not supported yet"));
-
- value!(option_none: Option::<&str>::None => "");
- value!(option_some: Some("non-escaped string") => "non-escaped string");
- value!(option_some_empty_str: Some("") => "");
-
- value!(unit: () => "");
- value!(unit_struct: Unit => "");
- value!(unit_struct_escaped: UnitEscaped => "");
-
- value!(enum_unit: Enum::Unit => "");
- err!(enum_unit_escaped:
- SpecialEnum::Value {
- before: "answer",
- content: Enum::UnitEscaped,
- after: "answer",
+ => r#""#);
+ serialize_as!(some_non_empty:
+ OptionalAttributes {
+ a: Some("1"),
+ b: Some("2"),
}
- => Unsupported("character `<` is not allowed at the start of an XML name `<\"&'>`"));
+ => r#""#);
+ }
+ }
- value!(newtype: Newtype(42) => "42");
- value!(enum_newtype: Enum::Newtype(42) => "42");
-
- // Note that sequences of primitives serialized without delimiters!
- value!(seq: vec![1, 2, 3] => "123");
- value!(seq_empty: Vec::::new() => "");
- value!(tuple: ("<\"&'>", "with\t\n\r spaces", 3usize)
- => "<"&'>\
- with\t\n\r spaces\
- 3");
- value!(tuple_struct: Tuple("first", 42) => "first42");
- value!(enum_tuple: Enum::Tuple("first", 42)
- => "first\
- 42");
-
- // We cannot wrap map or struct in any container and should not
- // flatten it, so it is impossible to serialize maps and structs
- err!(map:
- SpecialEnum::Value {
- before: "answer",
- content: BTreeMap::from([("_1", 2), ("_3", 4)]),
- after: "answer",
- }
- => Unsupported("serialization of map types is not supported in `$value` field"));
- err!(struct_:
- SpecialEnum::Value {
- before: "answer",
- content: Struct { key: "answer", val: (42, 42) },
- after: "answer",
- }
- => Unsupported("serialization of struct `Struct` is not supported in `$value` field"));
- value!(enum_struct:
- Enum::Struct { key: "answer", val: (42, 42) }
- => "\
- answer\
- 42\
- 42\
- ");
- }
- }
-
- mod attributes {
- use super::*;
- use pretty_assertions::assert_eq;
-
- serialize_as!(map_attr: BTreeMap::from([("@key1", 1), ("@key2", 2)])
- => r#""#);
- serialize_as!(map_mixed: BTreeMap::from([("@key1", 1), ("key2", 2)])
- => r#"2"#);
-
- serialize_as!(struct_: Attributes { key: "answer", val: (42, 42) }
- => r#""#);
- serialize_as!(struct_before: AttributesBefore { key: "answer", val: 42 }
- => r#"42"#);
- serialize_as!(struct_after: AttributesAfter { key: "answer", val: 42 }
- => r#"answer"#);
-
- serialize_as!(enum_: Enum::Attributes { key: "answer", val: (42, 42) }
- => r#""#);
- serialize_as!(enum_before: Enum::AttributesBefore { key: "answer", val: 42 }
- => r#"42"#);
- serialize_as!(enum_after: Enum::AttributesAfter { key: "answer", val: 42 }
- => r#"answer"#);
-
- /// Test for https://github.com/tafia/quick-xml/issues/252
- mod optional {
- use super::*;
- use pretty_assertions::assert_eq;
-
- serialize_as!(none:
- OptionalAttributes { a: None, b: None }
- => r#""#);
- serialize_as!(some_empty_str:
- OptionalAttributes {
- a: Some(""),
- b: Some(""),
- }
- => r#""#);
- serialize_as!(some_non_empty:
- OptionalAttributes {
- a: Some("1"),
- b: Some("2"),
- }
- => r#""#);
- }
- }
-
- /// Test for https://github.com/tafia/quick-xml/issues/252
- mod optional {
- use super::*;
- use pretty_assertions::assert_eq;
+ /// Test for https://github.com/tafia/quick-xml/issues/252
+ mod optional {
+ use super::*;
+ use pretty_assertions::assert_eq;
serialize_as!(none:
OptionalElements { a: None, b: None }
@@ -1634,12 +1406,12 @@ mod tests {
serialize_as!(unit_struct: Unit => "");
serialize_as!(unit_struct_escaped: UnitEscaped => "");
- serialize_as!(enum_unit: Enum::Unit => "");
- err!(enum_unit_escaped: Enum::UnitEscaped
- => Unsupported("character `<` is not allowed at the start of an XML name `<\"&'>`"));
+ serialize_as!(enum_unit: Enum::Unit => "Unit");
+ serialize_as!(enum_unit_escaped: Enum::UnitEscaped => "<"&'>");
serialize_as!(newtype: Newtype(42) => "42");
- serialize_as!(enum_newtype: Enum::Newtype(42) => "42");
+ err!(enum_newtype: Enum::Newtype(42)
+ => Unsupported("cannot serialize enum newtype variant `Enum::Newtype`"));
serialize_as!(seq: vec![1, 2, 3]
=> "1\n\
@@ -1653,9 +1425,8 @@ mod tests {
serialize_as!(tuple_struct: Tuple("first", 42)
=> "first\n\
42");
- serialize_as!(enum_tuple: Enum::Tuple("first", 42)
- => "first\n\
- 42");
+ err!(enum_tuple: Enum::Tuple("first", 42)
+ => Unsupported("cannot serialize enum tuple variant `Enum::Tuple`"));
serialize_as!(map: BTreeMap::from([("_1", 2), ("_3", 4)])
=> "\n \
@@ -1668,16 +1439,12 @@ mod tests {
42\n \
42\n\
");
- serialize_as!(enum_struct: Enum::Struct { key: "answer", val: (42, 42) }
- => "\n \
- answer\n \
- 42\n \
- 42\n\
- ");
+ err!(enum_struct: Enum::Struct { key: "answer", val: (42, 42) }
+ => Unsupported("cannot serialize enum struct variant `Enum::Struct`"));
/// Special field name `$text` should be serialized as text content.
/// Sequences serialized as an `xs:list` content
- mod text {
+ mod text_field {
use super::*;
/// `$text` key in a map
@@ -1761,7 +1528,7 @@ mod tests {
content: Enum::Newtype(42),
after: "answer",
}
- => Unsupported("cannot serialize enum newtype variant `Enum::Newtype` as an attribute or text content value"));
+ => Unsupported("cannot serialize enum newtype variant `Enum::Newtype` as text content value"));
// Sequences are serialized separated by spaces, all spaces inside are escaped
text!(seq: vec![1, 2, 3] => "1 2 3");
@@ -1778,7 +1545,7 @@ mod tests {
content: Enum::Tuple("first", 42),
after: "answer",
}
- => Unsupported("cannot serialize enum tuple variant `Enum::Tuple` as an attribute or text content value"));
+ => Unsupported("cannot serialize enum tuple variant `Enum::Tuple` as text content value"));
// Complex types cannot be serialized in `$text` field
err!(map:
@@ -1787,21 +1554,21 @@ mod tests {
content: BTreeMap::from([("_1", 2), ("_3", 4)]),
after: "answer",
}
- => Unsupported("cannot serialize map as an attribute or text content value"));
+ => Unsupported("cannot serialize map as text content value"));
err!(struct_:
Text {
before: "answer",
content: Struct { key: "answer", val: (42, 42) },
after: "answer",
}
- => Unsupported("cannot serialize struct `Struct` as an attribute or text content value"));
+ => Unsupported("cannot serialize struct `Struct` as text content value"));
err!(enum_struct:
Text {
before: "answer",
content: Enum::Struct { key: "answer", val: (42, 42) },
after: "answer",
}
- => Unsupported("cannot serialize enum struct variant `Enum::Struct` as an attribute or text content value"));
+ => Unsupported("cannot serialize enum struct variant `Enum::Struct` as text content value"));
}
/// `$text` field inside a struct
@@ -1900,7 +1667,7 @@ mod tests {
content: Enum::Newtype(42),
after: "answer",
}
- => Unsupported("cannot serialize enum newtype variant `Enum::Newtype` as an attribute or text content value"));
+ => Unsupported("cannot serialize enum newtype variant `Enum::Newtype` as text content value"));
// Sequences are serialized separated by spaces, all spaces inside are escaped
text!(seq: vec![1, 2, 3] => "1 2 3");
@@ -1917,7 +1684,7 @@ mod tests {
content: Enum::Tuple("first", 42),
after: "answer",
}
- => Unsupported("cannot serialize enum tuple variant `Enum::Tuple` as an attribute or text content value"));
+ => Unsupported("cannot serialize enum tuple variant `Enum::Tuple` as text content value"));
// Complex types cannot be serialized in `$text` field
err!(map:
@@ -1926,167 +1693,28 @@ mod tests {
content: BTreeMap::from([("_1", 2), ("_3", 4)]),
after: "answer",
}
- => Unsupported("cannot serialize map as an attribute or text content value"));
+ => Unsupported("cannot serialize map as text content value"));
err!(struct_:
Text {
before: "answer",
content: Struct { key: "answer", val: (42, 42) },
after: "answer",
}
- => Unsupported("cannot serialize struct `Struct` as an attribute or text content value"));
+ => Unsupported("cannot serialize struct `Struct` as text content value"));
err!(enum_struct:
Text {
before: "answer",
content: Enum::Struct { key: "answer", val: (42, 42) },
after: "answer",
}
- => Unsupported("cannot serialize enum struct variant `Enum::Struct` as an attribute or text content value"));
- }
-
- /// `$text` field inside a struct variant of an enum
- mod enum_struct {
- use super::*;
- use pretty_assertions::assert_eq;
-
- macro_rules! text {
- ($name:ident: $data:expr) => {
- serialize_as!($name:
- SpecialEnum::Text {
- before: "answer",
- content: $data,
- after: "answer",
- }
- => "\n \
- answer\n \
- answer\n\
- ");
- };
- ($name:ident: $data:expr => $expected:literal) => {
- serialize_as!($name:
- SpecialEnum::Text {
- before: "answer",
- content: $data,
- after: "answer",
- }
- => concat!(
- "\n answer\n ",
- $expected,
- "\n answer\n",
- ));
- };
- }
-
- text!(false_: false => "false");
- text!(true_: true => "true");
-
- text!(i8_: -42i8 => "-42");
- text!(i16_: -4200i16 => "-4200");
- text!(i32_: -42000000i32 => "-42000000");
- text!(i64_: -42000000000000i64 => "-42000000000000");
- text!(isize_: -42000000000000isize => "-42000000000000");
-
- text!(u8_: 42u8 => "42");
- text!(u16_: 4200u16 => "4200");
- text!(u32_: 42000000u32 => "42000000");
- text!(u64_: 42000000000000u64 => "42000000000000");
- text!(usize_: 42000000000000usize => "42000000000000");
-
- serde_if_integer128! {
- text!(i128_: -420000000000000000000000000000i128 => "-420000000000000000000000000000");
- text!(u128_: 420000000000000000000000000000u128 => "420000000000000000000000000000");
- }
-
- text!(f32_: 4.2f32 => "4.2");
- text!(f64_: 4.2f64 => "4.2");
-
- text!(char_non_escaped: 'h' => "h");
- text!(char_lt: '<' => "<");
- text!(char_gt: '>' => ">");
- text!(char_amp: '&' => "&");
- text!(char_apos: '\'' => "'");
- text!(char_quot: '"' => """);
- //TODO: add a setting to escape leading/trailing spaces, in order to
- // pretty-print does not change the content
- text!(char_space: ' ' => " ");
-
- text!(str_non_escaped: "non-escaped string" => "non-escaped string");
- text!(str_escaped: "<\"escaped & string'>" => "<"escaped & string'>");
-
- err!(bytes:
- SpecialEnum::Text {
- before: "answer",
- content: Bytes(b"<\"escaped & bytes'>"),
- after: "answer",
- }
- => Unsupported("`serialize_bytes` not supported yet"));
-
- text!(option_none: Option::<&str>::None);
- text!(option_some: Some("non-escaped string") => "non-escaped string");
- text!(option_some_empty_str: Some(""));
-
- text!(unit: ());
- text!(unit_struct: Unit);
- text!(unit_struct_escaped: UnitEscaped);
-
- text!(enum_unit: Enum::Unit => "Unit");
- text!(enum_unit_escaped: Enum::UnitEscaped => "<"&'>");
-
- text!(newtype: Newtype(42) => "42");
- // We have no space where name of a variant can be stored
- err!(enum_newtype:
- SpecialEnum::Text {
- before: "answer",
- content: Enum::Newtype(42),
- after: "answer",
- }
- => Unsupported("cannot serialize enum newtype variant `Enum::Newtype` as an attribute or text content value"));
-
- // Sequences are serialized separated by spaces, all spaces inside are escaped
- text!(seq: vec![1, 2, 3] => "1 2 3");
- text!(seq_empty: Vec::::new());
- text!(tuple: ("<\"&'>", "with\t\n\r spaces", 3usize)
- => "<"&'> \
- with
spaces \
- 3");
- text!(tuple_struct: Tuple("first", 42) => "first 42");
- // We have no space where name of a variant can be stored
- err!(enum_tuple:
- SpecialEnum::Text {
- before: "answer",
- content: Enum::Tuple("first", 42),
- after: "answer",
- }
- => Unsupported("cannot serialize enum tuple variant `Enum::Tuple` as an attribute or text content value"));
-
- // Complex types cannot be serialized in `$text` field
- err!(map:
- SpecialEnum::Text {
- before: "answer",
- content: BTreeMap::from([("_1", 2), ("_3", 4)]),
- after: "answer",
- }
- => Unsupported("cannot serialize map as an attribute or text content value"));
- err!(struct_:
- SpecialEnum::Text {
- before: "answer",
- content: Struct { key: "answer", val: (42, 42) },
- after: "answer",
- }
- => Unsupported("cannot serialize struct `Struct` as an attribute or text content value"));
- err!(enum_struct:
- SpecialEnum::Text {
- before: "answer",
- content: Enum::Struct { key: "answer", val: (42, 42) },
- after: "answer",
- }
- => Unsupported("cannot serialize enum struct variant `Enum::Struct` as an attribute or text content value"));
+ => Unsupported("cannot serialize enum struct variant `Enum::Struct` as text content value"));
}
}
/// Special field name `$value` should be serialized using name, provided
/// by the type of value instead of a key. Sequences serialized as a list
/// of tags with that name (each element can have their own name)
- mod value {
+ mod value_field {
use super::*;
/// `$value` key in a map
@@ -2324,140 +1952,6 @@ mod tests {
42\n \
");
}
-
- /// `$value` field inside a struct variant of an enum
- mod enum_struct {
- use super::*;
- use pretty_assertions::assert_eq;
-
- macro_rules! value {
- ($name:ident: $data:expr) => {
- serialize_as!($name:
- SpecialEnum::Value {
- before: "answer",
- content: $data,
- after: "answer",
- }
- => "\n \
- answer\n \
- answer\n\
- ");
- };
- ($name:ident: $data:expr => $expected:literal) => {
- serialize_as!($name:
- SpecialEnum::Value {
- before: "answer",
- content: $data,
- after: "answer",
- }
- => concat!(
- "\n answer\n ",
- $expected,
- "\n answer\n",
- ));
- };
- }
-
- value!(false_: false => "false");
- value!(true_: true => "true");
-
- value!(i8_: -42i8 => "-42");
- value!(i16_: -4200i16 => "-4200");
- value!(i32_: -42000000i32 => "-42000000");
- value!(i64_: -42000000000000i64 => "-42000000000000");
- value!(isize_: -42000000000000isize => "-42000000000000");
-
- value!(u8_: 42u8 => "42");
- value!(u16_: 4200u16 => "4200");
- value!(u32_: 42000000u32 => "42000000");
- value!(u64_: 42000000000000u64 => "42000000000000");
- value!(usize_: 42000000000000usize => "42000000000000");
-
- serde_if_integer128! {
- value!(i128_: -420000000000000000000000000000i128 => "-420000000000000000000000000000");
- value!(u128_: 420000000000000000000000000000u128 => "420000000000000000000000000000");
- }
-
- value!(f32_: 4.2f32 => "4.2");
- value!(f64_: 4.2f64 => "4.2");
-
- value!(char_non_escaped: 'h' => "h");
- value!(char_lt: '<' => "<");
- value!(char_gt: '>' => ">");
- value!(char_amp: '&' => "&");
- value!(char_apos: '\'' => "'");
- value!(char_quot: '"' => """);
- //TODO: add a setting to escape leading/trailing spaces, in order to
- // pretty-print does not change the content
- value!(char_space: ' ' => " ");
-
- value!(str_non_escaped: "non-escaped string" => "non-escaped string");
- value!(str_escaped: "<\"escaped & string'>" => "<"escaped & string'>");
-
- err!(bytes:
- SpecialEnum::Value {
- before: "answer",
- content: Bytes(b"<\"escaped & bytes'>"),
- after: "answer",
- }
- => Unsupported("`serialize_bytes` not supported yet"));
-
- value!(option_none: Option::<&str>::None);
- value!(option_some: Some("non-escaped string") => "non-escaped string");
- value!(option_some_empty_str: Some(""));
-
- value!(unit: ());
- value!(unit_struct: Unit);
- value!(unit_struct_escaped: UnitEscaped);
-
- value!(enum_unit: Enum::Unit => "");
- err!(enum_unit_escaped:
- SpecialEnum::Value {
- before: "answer",
- content: Enum::UnitEscaped,
- after: "answer",
- }
- => Unsupported("character `<` is not allowed at the start of an XML name `<\"&'>`"));
-
- value!(newtype: Newtype(42) => "42");
- value!(enum_newtype: Enum::Newtype(42) => "42");
-
- // Note that sequences of primitives serialized without delimiters!
- value!(seq: vec![1, 2, 3] => "1\n 2\n 3");
- value!(seq_empty: Vec::::new());
- value!(tuple: ("<\"&'>", "with\t\n\r spaces", 3usize)
- => "<"&'>\n \
- with\t\n\r spaces\n \
- 3");
- value!(tuple_struct: Tuple("first", 42) => "first\n 42");
- value!(enum_tuple: Enum::Tuple("first", 42)
- => "first\n \
- 42");
-
- // We cannot wrap map or struct in any container and should not
- // flatten it, so it is impossible to serialize maps and structs
- err!(map:
- SpecialEnum::Value {
- before: "answer",
- content: BTreeMap::from([("_1", 2), ("_3", 4)]),
- after: "answer",
- }
- => Unsupported("serialization of map types is not supported in `$value` field"));
- err!(struct_:
- SpecialEnum::Value {
- before: "answer",
- content: Struct { key: "answer", val: (42, 42) },
- after: "answer",
- }
- => Unsupported("serialization of struct `Struct` is not supported in `$value` field"));
- value!(enum_struct:
- Enum::Struct { key: "answer", val: (42, 42) }
- => "\n \
- answer\n \
- 42\n \
- 42\n \
- ");
- }
}
mod attributes {
@@ -2482,16 +1976,8 @@ mod tests {
answer\n\
");
- serialize_as!(enum_: Enum::Attributes { key: "answer", val: (42, 42) }
- => r#""#);
- serialize_as!(enum_before: Enum::AttributesBefore { key: "answer", val: 42 }
- => "\n \
- 42\n\
- ");
- serialize_as!(enum_after: Enum::AttributesAfter { key: "answer", val: 42 }
- => "\n \
- answer\n\
- ");
+ err!(enum_: Enum::Attributes { key: "answer", val: (42, 42) }
+ => Unsupported("cannot serialize enum struct variant `Enum::Attributes`"));
/// Test for https://github.com/tafia/quick-xml/issues/252
mod optional {
@@ -2574,39 +2060,6 @@ mod tests {
};
}
- /// 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 = ElementSerializer {
- ser: ContentSerializer {
- writer: &mut buffer,
- level: QuoteLevel::Full,
- indent: Indent::None,
- write_indent: false,
- expand_empty_elements: false,
- },
- key: XmlName("root"),
- };
-
- match $data.serialize(ser).unwrap_err() {
- DeError::$kind(e) => assert_eq!(e, $reason),
- e => panic!(
- "Expected `{}({})`, found `{:?}`",
- stringify!($kind),
- $reason,
- e
- ),
- }
- // We can write something before fail
- // assert_eq!(buffer, "");
- }
- };
- }
-
serialize_as!(option_some_empty: Some("") => "");
serialize_as!(option_some_empty_str: Some("") => "");
@@ -2614,8 +2067,7 @@ mod tests {
serialize_as!(unit_struct: Unit => "");
serialize_as!(unit_struct_escaped: UnitEscaped => "");
- serialize_as!(enum_unit: Enum::Unit => "");
- err!(enum_unit_escaped: Enum::UnitEscaped
- => Unsupported("character `<` is not allowed at the start of an XML name `<\"&'>`"));
+ serialize_as!(enum_unit: Enum::Unit => "Unit");
+ serialize_as!(enum_unit_escaped: Enum::UnitEscaped => "<"&'>");
}
}
diff --git a/src/se/mod.rs b/src/se/mod.rs
index bbd3ce2e..839187f3 100644
--- a/src/se/mod.rs
+++ b/src/se/mod.rs
@@ -77,9 +77,11 @@ mod content;
mod element;
pub(crate) mod key;
pub(crate) mod simple_type;
+mod text;
use self::content::ContentSerializer;
-use self::element::ElementSerializer;
+use self::element::{ElementSerializer, Map, Struct, Tuple};
+use crate::de::TEXT_KEY;
use crate::errors::serialize::DeError;
use crate::writer::Indentation;
use serde::ser::{self, Serialize};
@@ -608,16 +610,13 @@ impl<'w, 'r, W: Write> ser::Serializer for Serializer<'w, 'r, W> {
type Ok = ();
type Error = DeError;
- type SerializeSeq = as ser::Serializer>::SerializeSeq;
- type SerializeTuple = as ser::Serializer>::SerializeTuple;
- type SerializeTupleStruct =
- as ser::Serializer>::SerializeTupleStruct;
- type SerializeTupleVariant =
- as ser::Serializer>::SerializeTupleVariant;
- type SerializeMap = as ser::Serializer>::SerializeMap;
- type SerializeStruct = as ser::Serializer>::SerializeStruct;
- type SerializeStructVariant =
- as ser::Serializer>::SerializeStructVariant;
+ type SerializeSeq = ElementSerializer<'w, 'r, W>;
+ type SerializeTuple = ElementSerializer<'w, 'r, W>;
+ type SerializeTupleStruct = ElementSerializer<'w, 'r, W>;
+ type SerializeTupleVariant = Tuple<'w, 'r, W>;
+ type SerializeMap = Map<'w, 'r, W>;
+ type SerializeStruct = Struct<'w, 'r, W>;
+ type SerializeStructVariant = Struct<'w, 'r, W>;
forward!(serialize_bool(bool));
@@ -662,11 +661,22 @@ impl<'w, 'r, W: Write> ser::Serializer for Serializer<'w, 'r, W> {
fn serialize_unit_variant(
self,
name: &'static str,
- variant_index: u32,
+ _variant_index: u32,
variant: &'static str,
) -> Result {
- self.ser_name(name)?
- .serialize_unit_variant(name, variant_index, variant)
+ if variant == TEXT_KEY {
+ // We should write some text but we don't known what text to write
+ Err(DeError::Unsupported(
+ format!(
+ "cannot serialize enum unit variant `{}::$text` as text content value",
+ name
+ )
+ .into(),
+ ))
+ } else {
+ let name = XmlName::try_from(variant)?;
+ self.ser.write_empty(name)
+ }
}
fn serialize_newtype_struct(
@@ -679,13 +689,21 @@ impl<'w, 'r, W: Write> ser::Serializer for Serializer<'w, 'r, W> {
fn serialize_newtype_variant(
self,
- name: &'static str,
- variant_index: u32,
+ _name: &'static str,
+ _variant_index: u32,
variant: &'static str,
value: &T,
) -> Result {
- self.ser_name(name)?
- .serialize_newtype_variant(name, variant_index, variant, value)
+ if variant == TEXT_KEY {
+ value.serialize(self.ser.into_simple_type_serializer())?;
+ Ok(())
+ } else {
+ let ser = ElementSerializer {
+ ser: self.ser,
+ key: XmlName::try_from(variant)?,
+ };
+ value.serialize(ser)
+ }
}
fn serialize_seq(self, len: Option) -> Result {
@@ -707,12 +725,22 @@ impl<'w, 'r, W: Write> ser::Serializer for Serializer<'w, 'r, W> {
fn serialize_tuple_variant(
self,
name: &'static str,
- variant_index: u32,
+ _variant_index: u32,
variant: &'static str,
len: usize,
) -> Result {
- self.ser_name(name)?
- .serialize_tuple_variant(name, variant_index, variant, len)
+ if variant == TEXT_KEY {
+ self.ser
+ .into_simple_type_serializer()
+ .serialize_tuple_struct(name, len)
+ .map(Tuple::Text)
+ } else {
+ let ser = ElementSerializer {
+ ser: self.ser,
+ key: XmlName::try_from(variant)?,
+ };
+ ser.serialize_tuple_struct(name, len).map(Tuple::Element)
+ }
}
fn serialize_map(self, len: Option) -> Result {
@@ -730,11 +758,24 @@ impl<'w, 'r, W: Write> ser::Serializer for Serializer<'w, 'r, W> {
fn serialize_struct_variant(
self,
name: &'static str,
- variant_index: u32,
+ _variant_index: u32,
variant: &'static str,
len: usize,
) -> Result {
- self.ser_name(name)?
- .serialize_struct_variant(name, variant_index, variant, len)
+ if variant == TEXT_KEY {
+ Err(DeError::Unsupported(
+ format!(
+ "cannot serialize enum struct variant `{}::$text` as text content value",
+ name
+ )
+ .into(),
+ ))
+ } else {
+ let ser = ElementSerializer {
+ ser: self.ser,
+ key: XmlName::try_from(variant)?,
+ };
+ ser.serialize_struct(name, len)
+ }
}
}
diff --git a/src/se/simple_type.rs b/src/se/simple_type.rs
index dad26b1b..e396606d 100644
--- a/src/se/simple_type.rs
+++ b/src/se/simple_type.rs
@@ -7,7 +7,8 @@ use crate::errors::serialize::DeError;
use crate::escapei::_escape;
use crate::se::{Indent, QuoteLevel};
use serde::ser::{
- Impossible, Serialize, SerializeSeq, SerializeTuple, SerializeTupleStruct, Serializer,
+ Impossible, Serialize, SerializeSeq, SerializeTuple, SerializeTupleStruct,
+ SerializeTupleVariant, Serializer,
};
use serde::serde_if_integer128;
use std::borrow::Cow;
@@ -612,6 +613,24 @@ impl<'i, W: Write> SerializeTupleStruct for SimpleSeq<'i, W> {
}
}
+impl<'i, W: Write> SerializeTupleVariant for SimpleSeq<'i, W> {
+ type Ok = W;
+ type Error = DeError;
+
+ #[inline]
+ fn serialize_field(&mut self, value: &T) -> Result<(), Self::Error>
+ where
+ T: ?Sized + Serialize,
+ {
+ SerializeSeq::serialize_element(self, value)
+ }
+
+ #[inline]
+ fn end(self) -> Result {
+ SerializeSeq::end(self)
+ }
+}
+
////////////////////////////////////////////////////////////////////////////////////////////////////
#[cfg(test)]
diff --git a/src/se/text.rs b/src/se/text.rs
new file mode 100644
index 00000000..4dec138d
--- /dev/null
+++ b/src/se/text.rs
@@ -0,0 +1,192 @@
+//! Contains serializer for a special `&text` field
+
+use crate::de::TEXT_KEY;
+use crate::errors::serialize::DeError;
+use crate::se::simple_type::{SimpleSeq, SimpleTypeSerializer};
+use serde::ser::{Impossible, Serialize, Serializer};
+use serde::serde_if_integer128;
+use std::fmt::Write;
+
+macro_rules! write_primitive {
+ ($method:ident ( $ty:ty )) => {
+ #[inline]
+ fn $method(self, value: $ty) -> Result {
+ self.0.$method(value)
+ }
+ };
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/// A serializer used to serialize a `$text` field of a struct or map.
+///
+/// This serializer a very similar to [`SimpleTypeSerializer`], but different
+/// from it in how it processes unit enum variants. Unlike [`SimpleTypeSerializer`]
+/// this serializer does not write anything for the unit variant.
+pub struct TextSerializer<'i, W: Write>(pub SimpleTypeSerializer<'i, W>);
+
+impl<'i, W: Write> Serializer for TextSerializer<'i, W> {
+ type Ok = W;
+ type Error = DeError;
+
+ type SerializeSeq = SimpleSeq<'i, W>;
+ type SerializeTuple = SimpleSeq<'i, W>;
+ type SerializeTupleStruct = SimpleSeq<'i, W>;
+ type SerializeTupleVariant = SimpleSeq<'i, W>;
+ type SerializeMap = Impossible;
+ type SerializeStruct = Impossible;
+ type SerializeStructVariant = Impossible;
+
+ write_primitive!(serialize_bool(bool));
+
+ 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));
+
+ write_primitive!(serialize_char(char));
+ write_primitive!(serialize_str(&str));
+ write_primitive!(serialize_bytes(&[u8]));
+
+ #[inline]
+ fn serialize_none(self) -> Result {
+ self.0.serialize_none()
+ }
+
+ fn serialize_some(self, value: &T) -> Result {
+ value.serialize(self)
+ }
+
+ #[inline]
+ fn serialize_unit(self) -> Result {
+ self.0.serialize_unit()
+ }
+
+ #[inline]
+ fn serialize_unit_struct(self, name: &'static str) -> Result {
+ self.0.serialize_unit_struct(name)
+ }
+
+ #[inline]
+ fn serialize_unit_variant(
+ self,
+ name: &'static str,
+ variant_index: u32,
+ variant: &'static str,
+ ) -> Result {
+ if variant == TEXT_KEY {
+ Ok(self.0.writer)
+ } else {
+ self.0.serialize_unit_variant(name, variant_index, variant)
+ }
+ }
+
+ fn serialize_newtype_struct(
+ self,
+ _name: &'static str,
+ value: &T,
+ ) -> Result {
+ value.serialize(self)
+ }
+
+ #[inline]
+ fn serialize_newtype_variant(
+ self,
+ name: &'static str,
+ _variant_index: u32,
+ variant: &'static str,
+ _value: &T,
+ ) -> Result {
+ Err(DeError::Unsupported(
+ format!(
+ "cannot serialize enum newtype variant `{}::{}` as text content value",
+ name, variant
+ )
+ .into(),
+ ))
+ }
+
+ #[inline]
+ fn serialize_seq(self, len: Option) -> Result {
+ self.0.serialize_seq(len)
+ }
+
+ #[inline]
+ fn serialize_tuple(self, len: usize) -> Result {
+ self.0.serialize_tuple(len)
+ }
+
+ #[inline]
+ fn serialize_tuple_struct(
+ self,
+ name: &'static str,
+ len: usize,
+ ) -> Result {
+ self.0.serialize_tuple_struct(name, len)
+ }
+
+ #[inline]
+ fn serialize_tuple_variant(
+ self,
+ name: &'static str,
+ _variant_index: u32,
+ variant: &'static str,
+ _len: usize,
+ ) -> Result {
+ Err(DeError::Unsupported(
+ format!(
+ "cannot serialize enum tuple variant `{}::{}` as text content value",
+ name, variant
+ )
+ .into(),
+ ))
+ }
+
+ #[inline]
+ fn serialize_map(self, _len: Option) -> Result {
+ Err(DeError::Unsupported(
+ "cannot serialize map as text content value".into(),
+ ))
+ }
+
+ #[inline]
+ fn serialize_struct(
+ self,
+ name: &'static str,
+ _len: usize,
+ ) -> Result {
+ Err(DeError::Unsupported(
+ format!("cannot serialize struct `{}` as text content value", name).into(),
+ ))
+ }
+
+ #[inline]
+ fn serialize_struct_variant(
+ self,
+ name: &'static str,
+ _variant_index: u32,
+ variant: &'static str,
+ _len: usize,
+ ) -> Result {
+ Err(DeError::Unsupported(
+ format!(
+ "cannot serialize enum struct variant `{}::{}` as text content value",
+ name, variant
+ )
+ .into(),
+ ))
+ }
+}
diff --git a/src/serde_helpers.rs b/src/serde_helpers.rs
index 779d733f..5e249870 100644
--- a/src/serde_helpers.rs
+++ b/src/serde_helpers.rs
@@ -199,11 +199,6 @@ macro_rules! impl_deserialize_for_internally_tagged_enum {
/// [`#[serde(with = "...")]`][with], [`#[serde(deserialize_with = "...")]`][de-with]
/// and [`#[serde(serialize_with = "...")]`][se-with].
///
-/// When you serialize unit variants of enums, they are serialized as an empty
-/// element, like ``. If your enum consist only of unit variants,
-/// it is frequently required to serialize them as string content of an
-/// element, like `Unit`. To make this possible use this module.
-///
/// ```
/// # use pretty_assertions::assert_eq;
/// use quick_xml::de::from_str;
@@ -258,6 +253,45 @@ macro_rules! impl_deserialize_for_internally_tagged_enum {
/// ```
/// Read about the meaning of a special [`$text`] field.
///
+/// In versions of quick-xml before 0.31.0 this module used to represent enum
+/// unit variants as `EnumUnitVariant` instead of ``.
+/// Since version 0.31.0 this is default representation of enums in normal fields,
+/// and `` requires `$value` field:
+///
+/// ```
+/// # use pretty_assertions::assert_eq;
+/// use quick_xml::de::from_str;
+/// use quick_xml::se::to_string;
+/// use serde::{Serialize, Deserialize};
+///
+/// #[derive(Serialize, Deserialize, PartialEq, Debug)]
+/// enum SomeEnum {
+/// // Default implementation serializes enum as an `` element
+/// EnumValue,
+/// # /*
+/// ...
+/// # */
+/// }
+///
+/// #[derive(Serialize, Deserialize, PartialEq, Debug)]
+/// #[serde(rename = "some-container")]
+/// struct SomeContainer {
+/// #[serde(rename = "$value")]
+/// field: SomeEnum,
+/// }
+///
+/// let container = SomeContainer {
+/// field: SomeEnum::EnumValue,
+/// };
+/// let xml = "\
+/// \
+/// \
+/// ";
+///
+/// assert_eq!(to_string(&container).unwrap(), xml);
+/// assert_eq!(from_str::(xml).unwrap(), container);
+/// ```
+///
/// [with]: https://serde.rs/field-attrs.html#with
/// [de-with]: https://serde.rs/field-attrs.html#deserialize_with
/// [se-with]: https://serde.rs/field-attrs.html#serialize_with
diff --git a/tests/serde-se.rs b/tests/serde-se.rs
index 0ea01a72..4b7fd43e 100644
--- a/tests/serde-se.rs
+++ b/tests/serde-se.rs
@@ -369,6 +369,14 @@ mod without_root {
mod enum_ {
use super::*;
+ /// Enum representation:
+ ///
+ /// |Kind |Top-level and in `$value` field |
+ /// |-------|-----------------------------------------|
+ /// |Unit |`` |
+ /// |Newtype|`42` |
+ /// |Tuple |`42answer` |
+ /// |Struct |`42
answer`|
mod externally_tagged {
use super::*;
use pretty_assertions::assert_eq;
@@ -432,8 +440,17 @@ mod without_root {
answer\
");
- /// Test serialization of the specially named variant `$text`
- mod text {
+ /// Test serialization of the specially named variant `$text`.
+ ///
+ /// Enum representation:
+ ///
+ /// |Kind |Top-level, in `$text` and `$value` fields|
+ /// |-------|-----------------------------------------|
+ /// |Unit |_(empty)_ |
+ /// |Newtype|`42` |
+ /// |Tuple |`42 answer` |
+ /// |Struct |Err(Unsupported) |
+ mod text_variant {
use super::*;
use pretty_assertions::assert_eq;
@@ -475,6 +492,155 @@ mod without_root {
string: "newtype text",
}
=> Unsupported("cannot serialize enum struct variant `Struct::$text` as text content value"));
+ /// Tests the enum type that is type of field of a struct.
+ /// The tests above does not cover those variants, because we use
+ /// different serializers for enums on top level and which represents
+ /// a field.
+ ///
+ /// According to general rules for structs, we should write `` tag
+ /// for a `field` field. Because value of that field is an enum, we should
+ /// somehow know what the variant was written in order to deserialize it,
+ /// but `$text` variant say us that we should write enum content using
+ /// `xs:simpleType` serialization.
+ ///
+ /// Enum representation:
+ ///
+ /// |Kind |In normal field `field`|
+ /// |-------|-----------------------|
+ /// |Unit |`` |
+ /// |Newtype|Err(Unsupported) [^1] |
+ /// |Tuple |Err(Unsupported) [^1] |
+ /// |Struct |Err(Unsupported) |
+ ///
+ /// [^1]: Unfortunately, cannot be represented, because the possible representation
+ /// (`42` and `42 answer`) will clash with
+ /// representation of normal unit variant in normal field
+ mod normal_field {
+ use super::*;
+ use super::{Newtype, Struct, Tuple, Unit};
+ use pretty_assertions::assert_eq;
+
+ // `Root::field` contains text content, and because text content is empty,
+ // `` is written
+ serialize_as!(unit: Root { field: Unit::Text } => "");
+ err!(newtype:
+ Root { field: Newtype::Text("newtype text") }
+ => Unsupported("cannot serialize enum newtype variant `Newtype::$text`"),
+ " Unsupported("cannot serialize enum tuple variant `Tuple::$text`"),
+ " Unsupported("cannot serialize enum struct variant `Struct::$text`"),
+ "` tags.
+ ///
+ /// Enum representation:
+ ///
+ /// |Kind |Top-level and in `$value` field|
+ /// |-------|-------------------------------|
+ /// |Unit |_(empty)_ |
+ /// |Newtype|`42` |
+ /// |Tuple |`42 answer` |
+ /// |Struct |Err(Unsupported) |
+ mod value_field {
+ use super::*;
+ use super::{Newtype, Struct, Tuple, Unit};
+ use pretty_assertions::assert_eq;
+
+ #[derive(Debug, PartialEq, Deserialize, Serialize)]
+ struct Root {
+ #[serde(rename = "$value")]
+ field: T,
+ }
+
+ // Without #[serde(default)] on a field we cannot deserialize value
+ // back, because there is no signs in the XML that `field` was written.
+ // If we write the usual enum, then variant name would be written as
+ // a tag, but because variant is a `$text`, nothing is written
+ serialize_as_only!(unit: Root { field: Unit::Text } => "");
+ serialize_as!(newtype:
+ Root { field: Newtype::Text("newtype text") }
+ => "newtype text");
+ serialize_as!(tuple:
+ Root { field: Tuple::Text(42.0, "tuple-text".into()) }
+ => "42 tuple-text");
+ // Note, that spaces in strings, even escaped, would represent
+ // the list item delimiters. Non-symmetric serialization follows
+ // tradition: the XmlBeans Java library have the same behavior.
+ // See also
+ serialize_as_only!(tuple_with_spaces:
+ Root { field: Tuple::Text(42.0, "tuple text".into()) }
+ => "42 tuple text");
+ err!(struct_:
+ Root { field: Struct::Text {
+ float: 42.0,
+ string: "answer"
+ }}
+ => Unsupported("cannot serialize `$text` struct variant of `Struct` enum"),
+ " {
+ #[serde(rename = "$text")]
+ field: T,
+ }
+
+ // Without #[serde(default)] on a field we cannot deserialize value
+ // back, because there is no signs in the XML that `field` was written.
+ // If we write the usual enum, then variant name would be written as
+ // a tag, but because variant is a `$text`, nothing is written
+ serialize_as_only!(unit: Root { field: Unit::Text } => "");
+ err!(newtype:
+ Root { field: Newtype::Text("newtype text") }
+ => Unsupported("cannot serialize enum newtype variant `Newtype::$text` as text content value"),
+ " Unsupported("cannot serialize enum tuple variant `Tuple::$text` as text content value"),
+ " Unsupported("cannot serialize enum struct variant `Struct::$text` as text content value"),
+ "Unit`|
+ /// |Newtype|Err(Unsupported) |
+ /// |Tuple |Err(Unsupported) |
+ /// |Struct |Err(Unsupported) |
+ mod normal_field {
use super::*;
use pretty_assertions::assert_eq;
- serialize_as_only!(unit:
+ serialize_as!(unit:
Root { field: ExternallyTagged::Unit }
=> "\
- \
+ Unit\
");
- serialize_as_only!(newtype:
+ err!(newtype:
Root { field: ExternallyTagged::Newtype(true) }
- => "\
- true\
- ");
- serialize_as_only!(tuple:
+ => Unsupported("cannot serialize enum newtype variant `ExternallyTagged::Newtype`"),
+ " "\
- 42\
- answer\
- ");
- serialize_as_only!(struct_:
+ => Unsupported("cannot serialize enum tuple variant `ExternallyTagged::Tuple`"),
+ " "\
- \
- 42\
- answer\
- \
- ");
- serialize_as_only!(nested_struct:
- Root { field: ExternallyTagged::Holder {
- nested: Nested { float: 42.0 },
- string: "answer",
- }}
- => "\
- \
- \
- 42\
- \
- answer\
- \
- ");
- serialize_as_only!(flatten_struct:
- Root { field: ExternallyTaggedWorkaround::Flatten {
- nested: Nested { float: 42.0 },
- string: "answer",
- }}
- => "\
- \
- 42\
- answer\
- \
- ");
- serialize_as_only!(empty_struct:
+ => Unsupported("cannot serialize enum struct variant `ExternallyTagged::Struct`"),
+ " "\
- \
- ");
- serialize_as_only!(text:
- Root { field: ExternallyTagged::Text {
- float: 42.0,
- string: "answer"
- }}
- => "\
- \
- 42\
- answer\
- \
- ");
+ => Unsupported("cannot serialize enum struct variant `ExternallyTagged::Empty`"),
+ " "\
\
- \
+ Unit\
\
");
- serialize_as_only!(newtype:
+ err!(newtype:
Root { field: Inner { inner: ExternallyTagged::Newtype(true) } }
- => "\
- \
- true\
- \
- ");
- serialize_as_only!(tuple:
+ => Unsupported("cannot serialize enum newtype variant `ExternallyTagged::Newtype`"),
+ " "\
- \
- 42\
- answer\
- \
- ");
- serialize_as_only!(struct_:
+ => Unsupported("cannot serialize enum tuple variant `ExternallyTagged::Tuple`"),
+ " "\
- \
- \
- 42\
- answer\
- \
- \
- ");
- serialize_as_only!(nested_struct:
- Root { field: Inner { inner: ExternallyTagged::Holder {
- nested: Nested { float: 42.0 },
- string: "answer",
- }}}
- => "\
- \
- \
- \
- 42\
- \
- answer\
- \
- \
- ");
- serialize_as_only!(flatten_struct:
- Root { field: Inner { inner: ExternallyTaggedWorkaround::Flatten {
- nested: Nested { float: 42.0 },
- string: "answer",
- }}}
- => "\
- \
- \
- 42\
- answer\
- \
- \
- ");
- serialize_as_only!(empty_struct:
+ => Unsupported("cannot serialize enum struct variant `ExternallyTagged::Struct`"),
+ " "\
- \
- \
- \
- ");
- serialize_as_only!(text:
- Root { field: Inner { inner: ExternallyTagged::Text {
- float: 42.0,
- string: "answer"
- }}}
- => "\
- \
- \
- 42\
- answer\
- \
- \
- ");
+ => Unsupported("cannot serialize enum struct variant `ExternallyTagged::Empty`"),
+ "` |
+ /// |Newtype|`42` |
+ /// |Tuple |`42answer` |
+ /// |Struct |`42
answer`|
+ mod value_field {
use super::*;
use pretty_assertions::assert_eq;
@@ -742,8 +826,8 @@ mod without_root {
");
}
- /// The same tests as in `in_struct2`, but enum field renamed to `$value`.
- mod in_struct_value2 {
+ /// The same tests as in `normal_field2`, but enum field renamed to `$value`.
+ mod value_field2 {
use super::*;
use pretty_assertions::assert_eq;
@@ -840,10 +924,19 @@ mod without_root {
");
}
- /// The same tests as in `in_struct`, but enum field renamed to `$text`.
+ /// The same tests as in `normal_field`, but enum field renamed to `$text`.
///
/// Text representation of enum is possible only for unit variants.
- mod in_struct_text {
+ ///
+ /// Enum representation:
+ ///
+ /// |Kind |In `$text` field|
+ /// |-------|----------------|
+ /// |Unit |`Unit` |
+ /// |Newtype|Err(Unsupported)|
+ /// |Tuple |Err(Unsupported)|
+ /// |Struct |Err(Unsupported)|
+ mod text_field {
use super::*;
use pretty_assertions::assert_eq;
@@ -858,25 +951,25 @@ mod without_root {
=> "Unit");
err!(newtype:
Root { field: ExternallyTagged::Newtype(true) }
- => Unsupported("cannot serialize enum newtype variant `ExternallyTagged::Newtype` as an attribute or text content value"),
+ => Unsupported("cannot serialize enum newtype variant `ExternallyTagged::Newtype` as text content value"),
" Unsupported("cannot serialize enum tuple variant `ExternallyTagged::Tuple` as an attribute or text content value"),
+ => Unsupported("cannot serialize enum tuple variant `ExternallyTagged::Tuple` as text content value"),
" Unsupported("cannot serialize enum struct variant `ExternallyTagged::Struct` as an attribute or text content value"),
+ => Unsupported("cannot serialize enum struct variant `ExternallyTagged::Struct` as text content value"),
" Unsupported("cannot serialize enum struct variant `ExternallyTagged::Holder` as an attribute or text content value"),
+ => Unsupported("cannot serialize enum struct variant `ExternallyTagged::Holder` as text content value"),
" Unsupported("cannot serialize enum newtype variant `ExternallyTaggedWorkaround::Flatten` as an attribute or text content value"),
+ => Unsupported("cannot serialize enum newtype variant `ExternallyTaggedWorkaround::Flatten` as text content value"),
" Unsupported("cannot serialize enum struct variant `ExternallyTagged::Empty` as an attribute or text content value"),
+ => Unsupported("cannot serialize enum struct variant `ExternallyTagged::Empty` as text content value"),
" Unsupported("cannot serialize enum struct variant `ExternallyTagged::Text` as an attribute or text content value"),
+ => Unsupported("cannot serialize enum struct variant `ExternallyTagged::Text` as text content value"),
" "Unit");
err!(newtype:
Root { field: Inner { inner: ExternallyTagged::Newtype(true) } }
- => Unsupported("cannot serialize enum newtype variant `ExternallyTagged::Newtype` as an attribute or text content value"),
+ => Unsupported("cannot serialize enum newtype variant `ExternallyTagged::Newtype` as text content value"),
" Unsupported("cannot serialize enum tuple variant `ExternallyTagged::Tuple` as an attribute or text content value"),
+ => Unsupported("cannot serialize enum tuple variant `ExternallyTagged::Tuple` as text content value"),
" Unsupported("cannot serialize enum struct variant `ExternallyTagged::Struct` as an attribute or text content value"),
+ => Unsupported("cannot serialize enum struct variant `ExternallyTagged::Struct` as text content value"),
" Unsupported("cannot serialize enum struct variant `ExternallyTagged::Holder` as an attribute or text content value"),
+ => Unsupported("cannot serialize enum struct variant `ExternallyTagged::Holder` as text content value"),
" Unsupported("cannot serialize enum newtype variant `ExternallyTaggedWorkaround::Flatten` as an attribute or text content value"),
+ => Unsupported("cannot serialize enum newtype variant `ExternallyTaggedWorkaround::Flatten` as text content value"),
" Unsupported("cannot serialize enum struct variant `ExternallyTagged::Empty` as an attribute or text content value"),
+ => Unsupported("cannot serialize enum struct variant `ExternallyTagged::Empty` as text content value"),
" Unsupported("cannot serialize enum struct variant `ExternallyTagged::Text` as an attribute or text content value"),
+ => Unsupported("cannot serialize enum struct variant `ExternallyTagged::Text` as text content value"),
"