Skip to content

Commit 569ac22

Browse files
authored
Merge pull request #669 from Mingun/fix-serde181-compatibility
Fix serde 1.0.181 compatibility
2 parents 27a93b3 + 78b7bc0 commit 569ac22

File tree

11 files changed

+1458
-1017
lines changed

11 files changed

+1458
-1017
lines changed

Cargo.toml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,7 @@ include = ["src/*", "LICENSE-MIT.md", "README.md"]
1616
[dependencies]
1717
document-features = { version = "0.2", optional = true }
1818
encoding_rs = { version = "0.8", optional = true }
19-
# FIXME: remove upper bound when https://github.com/tafia/quick-xml/issues/630 is resolved
20-
serde = { version = ">=1.0.100,<1.0.181", optional = true }
19+
serde = { version = ">=1.0.100", optional = true }
2120
tokio = { version = "1.10", optional = true, default-features = false, features = ["io-util"] }
2221
memchr = "2.1"
2322
arbitrary = { version = "1", features = ["derive"], optional = true }

Changelog.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@
1212

1313
MSRV bumped to 1.56! Crate now uses Rust 2021 edition.
1414

15+
Enum representation was changed (it was buggy anyway) to ensure compatibility with
16+
serde >= 1.0.181
17+
1518
### New Features
1619

1720
- [#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.
4043
(and newly added `ElementWriter::write_inner_content_async` of course).
4144
- [#662]: Get rid of some allocations during serde deserialization.
4245
- [#665]: Improve serialization of `xs:list`s when some elements serialized to an empty string.
46+
- [#630]: Fixed compatibility with serde >= 1.0.181
4347

4448
[#545]: https://github.com/tafia/quick-xml/pull/545
4549
[#567]: https://github.com/tafia/quick-xml/issues/567
4650
[#580]: https://github.com/tafia/quick-xml/issues/580
4751
[#619]: https://github.com/tafia/quick-xml/issues/619
52+
[#630]: https://github.com/tafia/quick-xml/issues/630
4853
[#635]: https://github.com/tafia/quick-xml/pull/635
4954
[#643]: https://github.com/tafia/quick-xml/pull/643
5055
[#649]: https://github.com/tafia/quick-xml/pull/646

src/de/map.rs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -613,7 +613,27 @@ where
613613
where
614614
V: Visitor<'de>,
615615
{
616-
visitor.visit_enum(self)
616+
if self.fixed_name {
617+
match self.map.de.next()? {
618+
// Handles <field>UnitEnumVariant</field>
619+
DeEvent::Start(_) => {
620+
// skip <field>, read text after it and ensure that it is ended by </field>
621+
let text = self.map.de.read_text()?;
622+
if text.is_empty() {
623+
// Map empty text (<field/>) to a special `$text` variant
624+
visitor.visit_enum(SimpleTypeDeserializer::from_text(TEXT_KEY.into()))
625+
} else {
626+
visitor.visit_enum(SimpleTypeDeserializer::from_text(text))
627+
}
628+
}
629+
// SAFETY: we use that deserializer with `fixed_name == true`
630+
// only from the `MapAccess::next_value_seed` and only when we
631+
// peeked `Start` event
632+
_ => unreachable!(),
633+
}
634+
} else {
635+
visitor.visit_enum(self)
636+
}
617637
}
618638

619639
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>

src/de/mod.rs

Lines changed: 86 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@
2020
//! - [Choices (`xs:choice` XML Schema type)](#choices-xschoice-xml-schema-type)
2121
//! - [Sequences (`xs:all` and `xs:sequence` XML Schema types)](#sequences-xsall-and-xssequence-xml-schema-types)
2222
//! - [Composition Rules](#composition-rules)
23+
//! - [Enum Representations](#enum-representations)
24+
//! - [Normal enum variant](#normal-enum-variant)
25+
//! - [`$text` enum variant](#text-enum-variant)
2326
//! - [Difference between `$text` and `$value` special names](#difference-between-text-and-value-special-names)
2427
//! - [`$text`](#text)
2528
//! - [`$value`](#value)
@@ -29,7 +32,6 @@
2932
//! - [Frequently Used Patterns](#frequently-used-patterns)
3033
//! - [`<element>` lists](#element-lists)
3134
//! - [Overlapped (Out-of-Order) Elements](#overlapped-out-of-order-elements)
32-
//! - [Enum::Unit Variants As a Text](#enumunit-variants-as-a-text)
3335
//! - [Internally Tagged Enums](#internally-tagged-enums)
3436
//!
3537
//!
@@ -1351,6 +1353,58 @@
13511353
//!
13521354
//!
13531355
//!
1356+
//! Enum Representations
1357+
//! ====================
1358+
//!
1359+
//! `quick-xml` represents enums differently in normal fields, `$text` fields and
1360+
//! `$value` fields. A normal representation is compatible with serde's adjacent
1361+
//! and internal tags feature -- tag for adjacently and internally tagged enums
1362+
//! are serialized using [`Serializer::serialize_unit_variant`] and deserialized
1363+
//! using [`Deserializer::deserialize_enum`].
1364+
//!
1365+
//! Use those simple rules to remember, how enum would be represented in XML:
1366+
//! - In `$value` field the representation is always the same as top-level representation;
1367+
//! - In `$text` field the representation is always the same as in normal field,
1368+
//! but surrounding tags with field name are removed;
1369+
//! - In normal field the representation is always contains a tag with a field name.
1370+
//!
1371+
//! Normal enum variant
1372+
//! -------------------
1373+
//!
1374+
//! To model an `xs:choice` XML construct use `$value` field.
1375+
//! To model a top-level `xs:choice` just use the enum type.
1376+
//!
1377+
//! |Kind |Top-level and in `$value` field |In normal field |In `$text` field |
1378+
//! |-------|-----------------------------------------|---------------------|---------------------|
1379+
//! |Unit |`<Unit/>` |`<field>Unit</field>`|`Unit` |
1380+
//! |Newtype|`<Newtype>42</Newtype>` |Err(Unsupported) |Err(Unsupported) |
1381+
//! |Tuple |`<Tuple>42</Tuple><Tuple>answer</Tuple>` |Err(Unsupported) |Err(Unsupported) |
1382+
//! |Struct |`<Struct><q>42</q><a>answer</a></Struct>`|Err(Unsupported) |Err(Unsupported) |
1383+
//!
1384+
//! `$text` enum variant
1385+
//! --------------------
1386+
//!
1387+
//! |Kind |Top-level and in `$value` field |In normal field |In `$text` field |
1388+
//! |-------|-----------------------------------------|---------------------|---------------------|
1389+
//! |Unit |_(empty)_ |`<field/>` |_(empty)_ |
1390+
//! |Newtype|`42` |Err(Unsupported) [^1]|Err(Unsupported) [^2]|
1391+
//! |Tuple |`42 answer` |Err(Unsupported) [^3]|Err(Unsupported) [^4]|
1392+
//! |Struct |Err(Unsupported) |Err(Unsupported) |Err(Unsupported) |
1393+
//!
1394+
//! [^1]: If this serialize as `<field>42</field>` then it will be ambiguity during deserialization,
1395+
//! because it clash with `Unit` representation in normal field.
1396+
//!
1397+
//! [^2]: If this serialize as `42` then it will be ambiguity during deserialization,
1398+
//! because it clash with `Unit` representation in `$text` field.
1399+
//!
1400+
//! [^3]: If this serialize as `<field>42 answer</field>` then it will be ambiguity during deserialization,
1401+
//! because it clash with `Unit` representation in normal field.
1402+
//!
1403+
//! [^4]: If this serialize as `42 answer` then it will be ambiguity during deserialization,
1404+
//! because it clash with `Unit` representation in `$text` field.
1405+
//!
1406+
//!
1407+
//!
13541408
//! Difference between `$text` and `$value` special names
13551409
//! =====================================================
13561410
//!
@@ -1431,33 +1485,54 @@
14311485
//! get their names from the field name. It cannot be deserialized, because `Enum`
14321486
//! expects elements `<A/>`, `<B/>` or `<C/>`, but `AnyName` looked only for `<field/>`:
14331487
//!
1434-
//! ```no_run
1488+
//! ```
14351489
//! # use serde::{Deserialize, Serialize};
1490+
//! # use pretty_assertions::assert_eq;
1491+
//! # #[derive(PartialEq, Debug)]
14361492
//! #[derive(Deserialize, Serialize)]
14371493
//! enum Enum { A, B, C }
14381494
//!
1495+
//! # #[derive(PartialEq, Debug)]
14391496
//! #[derive(Deserialize, Serialize)]
14401497
//! struct AnyName {
1441-
//! // <field/>
1498+
//! // <field>A</field>, <field>B</field>, or <field>C</field>
14421499
//! field: Enum,
14431500
//! }
1501+
//! # assert_eq!(
1502+
//! # quick_xml::se::to_string(&AnyName { field: Enum::A }).unwrap(),
1503+
//! # "<AnyName><field>A</field></AnyName>",
1504+
//! # );
1505+
//! # assert_eq!(
1506+
//! # AnyName { field: Enum::B },
1507+
//! # quick_xml::de::from_str("<root><field>B</field></root>").unwrap(),
1508+
//! # );
14441509
//! ```
14451510
//!
14461511
//! If you rename field to `$value`, then `field` would be serialized as `<A/>`,
14471512
//! `<B/>` or `<C/>`, depending on the its content. It is also possible to
14481513
//! deserialize it from the same elements:
14491514
//!
1450-
//! ```no_run
1515+
//! ```
14511516
//! # use serde::{Deserialize, Serialize};
1452-
//! # #[derive(Deserialize, Serialize)]
1517+
//! # use pretty_assertions::assert_eq;
1518+
//! # #[derive(Deserialize, Serialize, PartialEq, Debug)]
14531519
//! # enum Enum { A, B, C }
14541520
//! #
1521+
//! # #[derive(PartialEq, Debug)]
14551522
//! #[derive(Deserialize, Serialize)]
14561523
//! struct AnyName {
14571524
//! // <A/>, <B/> or <C/>
14581525
//! #[serde(rename = "$value")]
14591526
//! field: Enum,
14601527
//! }
1528+
//! # assert_eq!(
1529+
//! # quick_xml::se::to_string(&AnyName { field: Enum::A }).unwrap(),
1530+
//! # "<AnyName><A/></AnyName>",
1531+
//! # );
1532+
//! # assert_eq!(
1533+
//! # AnyName { field: Enum::B },
1534+
//! # quick_xml::de::from_str("<root><B/></root>").unwrap(),
1535+
//! # );
14611536
//! ```
14621537
//!
14631538
//! ### Primitives and sequences of primitives
@@ -1467,6 +1542,7 @@
14671542
//!
14681543
//! ```
14691544
//! # use serde::{Deserialize, Serialize};
1545+
//! # use pretty_assertions::assert_eq;
14701546
//! # use quick_xml::de::from_str;
14711547
//! # use quick_xml::se::to_string;
14721548
//! #[derive(Deserialize, Serialize, PartialEq, Debug)]
@@ -1493,6 +1569,7 @@
14931569
//!
14941570
//! ```
14951571
//! # use serde::{Deserialize, Serialize};
1572+
//! # use pretty_assertions::assert_eq;
14961573
//! # use quick_xml::de::from_str;
14971574
//! # use quick_xml::se::to_string;
14981575
//! #[derive(Deserialize, Serialize, PartialEq, Debug)]
@@ -1516,6 +1593,7 @@
15161593
//!
15171594
//! ```
15181595
//! # use serde::{Deserialize, Serialize};
1596+
//! # use pretty_assertions::assert_eq;
15191597
//! # use quick_xml::de::from_str;
15201598
//! # use quick_xml::se::to_string;
15211599
//! #[derive(Deserialize, Serialize, PartialEq, Debug)]
@@ -1549,6 +1627,7 @@
15491627
//!
15501628
//! ```
15511629
//! # use serde::{Deserialize, Serialize};
1630+
//! # use pretty_assertions::assert_eq;
15521631
//! # use quick_xml::de::from_str;
15531632
//! # use quick_xml::se::to_string;
15541633
//! #[derive(Deserialize, Serialize, PartialEq, Debug)]
@@ -1708,75 +1787,6 @@
17081787
//! }
17091788
//! ```
17101789
//!
1711-
//! Enum::Unit Variants As a Text
1712-
//! -----------------------------
1713-
//! One frequent task and a typical mistake is to creation of mapping a text
1714-
//! content of some tag to a Rust `enum`. For example, for the XML:
1715-
//!
1716-
//! ```xml
1717-
//! <some-container>
1718-
//! <field>EnumValue</field>
1719-
//! </some-container>
1720-
//! ```
1721-
//! one could create an _incorrect_ mapping
1722-
//!
1723-
//! ```
1724-
//! # use serde::{Deserialize, Serialize};
1725-
//! #
1726-
//! #[derive(Serialize, Deserialize)]
1727-
//! enum SomeEnum {
1728-
//! EnumValue,
1729-
//! # /*
1730-
//! ...
1731-
//! # */
1732-
//! }
1733-
//!
1734-
//! #[derive(Serialize, Deserialize)]
1735-
//! #[serde(rename = "some-container")]
1736-
//! struct SomeContainer {
1737-
//! field: SomeEnum,
1738-
//! }
1739-
//! ```
1740-
//!
1741-
//! Actually, those types will be serialized into:
1742-
//! ```xml
1743-
//! <some-container>
1744-
//! <EnumValue/>
1745-
//! </some-container>
1746-
//! ```
1747-
//! and will not be able to be deserialized.
1748-
//!
1749-
//! You can easily see what's wrong if you think about attributes, which could
1750-
//! be defined in the `<field>` tag:
1751-
//! ```xml
1752-
//! <some-container>
1753-
//! <field some="attribute">EnumValue</field>
1754-
//! </some-container>
1755-
//! ```
1756-
//!
1757-
//! After that you can find the correct solution, using the principles explained
1758-
//! above. You should wrap `SomeEnum` into wrapper struct under the [`$text`](#text)
1759-
//! name:
1760-
//! ```
1761-
//! # use serde::{Serialize, Deserialize};
1762-
//! # type SomeEnum = ();
1763-
//! #[derive(Serialize, Deserialize)]
1764-
//! struct Field {
1765-
//! // Use a special name `$text` to map field to the text content
1766-
//! #[serde(rename = "$text")]
1767-
//! content: SomeEnum,
1768-
//! }
1769-
//!
1770-
//! #[derive(Serialize, Deserialize)]
1771-
//! #[serde(rename = "some-container")]
1772-
//! struct SomeContainer {
1773-
//! field: Field,
1774-
//! }
1775-
//! ```
1776-
//!
1777-
//! If you still want to keep your struct untouched, you can instead use the
1778-
//! helper module [`text_content`].
1779-
//!
17801790
//!
17811791
//! Internally Tagged Enums
17821792
//! -----------------------
@@ -1794,7 +1804,8 @@
17941804
//! [specification]: https://www.w3.org/TR/xmlschema11-1/#Simple_Type_Definition
17951805
//! [`deserialize_with`]: https://serde.rs/field-attrs.html#deserialize_with
17961806
//! [#497]: https://github.com/tafia/quick-xml/issues/497
1797-
//! [`text_content`]: crate::serde_helpers::text_content
1807+
//! [`Serializer::serialize_unit_variant`]: serde::Serializer::serialize_unit_variant
1808+
//! [`Deserializer::deserialize_enum`]: serde::Deserializer::deserialize_enum
17981809
//! [Tagged enums]: https://serde.rs/enum-representations.html#internally-tagged
17991810
//! [serde#1183]: https://github.com/serde-rs/serde/issues/1183
18001811
//! [serde#1495]: https://github.com/serde-rs/serde/issues/1495

0 commit comments

Comments
 (0)