diff --git a/src/de/mod.rs b/src/de/mod.rs index fbe6618c..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 |`42answer`|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 //! ===================================================== //! @@ -1733,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 //! ----------------------- @@ -1819,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/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