Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand Down
5 changes: 5 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down
22 changes: 21 additions & 1 deletion src/de/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -613,7 +613,27 @@ where
where
V: Visitor<'de>,
{
visitor.visit_enum(self)
if self.fixed_name {
match self.map.de.next()? {
// Handles <field>UnitEnumVariant</field>
DeEvent::Start(_) => {
// skip <field>, read text after it and ensure that it is ended by </field>
let text = self.map.de.read_text()?;
if text.is_empty() {
// Map empty text (<field/>) 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<V>(self, visitor: V) -> Result<V::Value, Self::Error>
Expand Down
161 changes: 86 additions & 75 deletions src/de/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -29,7 +32,6 @@
//! - [Frequently Used Patterns](#frequently-used-patterns)
//! - [`<element>` 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)
//!
//!
Expand Down Expand Up @@ -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/>` |`<field>Unit</field>`|`Unit` |
//! |Newtype|`<Newtype>42</Newtype>` |Err(Unsupported) |Err(Unsupported) |
//! |Tuple |`<Tuple>42</Tuple><Tuple>answer</Tuple>` |Err(Unsupported) |Err(Unsupported) |
//! |Struct |`<Struct><q>42</q><a>answer</a></Struct>`|Err(Unsupported) |Err(Unsupported) |
//!
//! `$text` enum variant
//! --------------------
//!
//! |Kind |Top-level and in `$value` field |In normal field |In `$text` field |
//! |-------|-----------------------------------------|---------------------|---------------------|
//! |Unit |_(empty)_ |`<field/>` |_(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 `<field>42</field>` 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 `<field>42 answer</field>` 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
//! =====================================================
//!
Expand Down Expand Up @@ -1431,33 +1485,54 @@
//! get their names from the field name. It cannot be deserialized, because `Enum`
//! expects elements `<A/>`, `<B/>` or `<C/>`, but `AnyName` looked only for `<field/>`:
//!
//! ```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 {
//! // <field/>
//! // <field>A</field>, <field>B</field>, or <field>C</field>
//! field: Enum,
//! }
//! # assert_eq!(
//! # quick_xml::se::to_string(&AnyName { field: Enum::A }).unwrap(),
//! # "<AnyName><field>A</field></AnyName>",
//! # );
//! # assert_eq!(
//! # AnyName { field: Enum::B },
//! # quick_xml::de::from_str("<root><field>B</field></root>").unwrap(),
//! # );
//! ```
//!
//! If you rename field to `$value`, then `field` would be serialized as `<A/>`,
//! `<B/>` or `<C/>`, 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 {
//! // <A/>, <B/> or <C/>
//! #[serde(rename = "$value")]
//! field: Enum,
//! }
//! # assert_eq!(
//! # quick_xml::se::to_string(&AnyName { field: Enum::A }).unwrap(),
//! # "<AnyName><A/></AnyName>",
//! # );
//! # assert_eq!(
//! # AnyName { field: Enum::B },
//! # quick_xml::de::from_str("<root><B/></root>").unwrap(),
//! # );
//! ```
//!
//! ### Primitives and sequences of primitives
Expand All @@ -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)]
Expand All @@ -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)]
Expand All @@ -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)]
Expand Down Expand Up @@ -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)]
Expand Down Expand Up @@ -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
//! <some-container>
//! <field>EnumValue</field>
//! </some-container>
//! ```
//! 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
//! <some-container>
//! <EnumValue/>
//! </some-container>
//! ```
//! 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 `<field>` tag:
//! ```xml
//! <some-container>
//! <field some="attribute">EnumValue</field>
//! </some-container>
//! ```
//!
//! 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
//! -----------------------
Expand All @@ -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
Expand Down
Loading