Skip to content

Commit e93605a

Browse files
Mingundralley
authored andcommitted
Implement deserialization textual content into externally tagged enum variants
1 parent 54055b7 commit e93605a

File tree

3 files changed

+129
-33
lines changed

3 files changed

+129
-33
lines changed

Changelog.md

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

1313
### New Features
1414

15+
- [#541]: Deserialize specially named `$text` enum variant in [externally tagged]
16+
enums from textual content
17+
1518
### Bug Fixes
1619

1720
- [#537]: Restore ability to deserialize attributes that represents XML namespace
1821
mappings (`xmlns:xxx`) that was broken since [#490]
1922

2023
### Misc Changes
2124

25+
[externally tagged]: https://serde.rs/enum-representations.html#externally-tagged
2226
[#490]: https://github.com/tafia/quick-xml/pull/490
2327
[#537]: https://github.com/tafia/quick-xml/issues/537
28+
[#541]: https://github.com/tafia/quick-xml/pull/541
2429

2530
## 0.27.1 -- 2022-12-28
2631

src/de/mod.rs

Lines changed: 52 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -473,7 +473,7 @@
473473
//! <!-- 8 ===================================================================================== -->
474474
//! <tr>
475475
//! <td>
476-
//! An XML with different root tag names:
476+
//! An XML with different root tag names, as well as text / CDATA content:
477477
//!
478478
//! ```xml
479479
//! <one field1="...">...</one>
@@ -483,12 +483,18 @@
483483
//! <field2>...</field2>
484484
//! </two>
485485
//! ```
486+
//! ```xml
487+
//! Text <![CDATA[or (mixed)
488+
//! CDATA]]> content
489+
//! ```
486490
//! </td>
487491
//! <td>
488492
//!
489493
//! An enum where each variant have a name of the possible root tag. The name of
490494
//! the enum itself does not matter.
491495
//!
496+
//! If you need to get a textual content, mark a variant with `#[serde(rename = "$text")]`.
497+
//!
492498
//! All these structs can be used to deserialize from any XML on the
493499
//! left side depending on amount of information that you want to get:
494500
//!
@@ -503,9 +509,19 @@
503509
//! enum AnyName {
504510
//! One { #[serde(rename = "@field1")] field1: T },
505511
//! Two { field2: U },
512+
//!
513+
//! /// Use unit variant, if you do not care of a content.
514+
//! /// You can use tuple variant if you want to parse
515+
//! /// textual content as an xs:list.
516+
//! /// Struct variants are not supported and will return
517+
//! /// Err(Unsupported)
518+
//! #[serde(rename = "$text")]
519+
//! Text(String),
506520
//! }
507521
//! # assert_eq!(AnyName::One { field1: () }, quick_xml::de::from_str(r#"<one field1="...">...</one>"#).unwrap());
508522
//! # assert_eq!(AnyName::Two { field2: () }, quick_xml::de::from_str(r#"<two><field2>...</field2></two>"#).unwrap());
523+
//! # assert_eq!(AnyName::Text("text".into()), quick_xml::de::from_str(r#"text"#).unwrap());
524+
//! # // TODO: After #474 parse mixed content
509525
//! ```
510526
//! ```
511527
//! # use pretty_assertions::assert_eq;
@@ -523,9 +539,13 @@
523539
//! // `field1` content discarded
524540
//! One,
525541
//! Two(Two),
542+
//! #[serde(rename = "$text")]
543+
//! Text,
526544
//! }
527545
//! # assert_eq!(AnyName::One, quick_xml::de::from_str(r#"<one field1="...">...</one>"#).unwrap());
528546
//! # assert_eq!(AnyName::Two(Two { field2: () }), quick_xml::de::from_str(r#"<two><field2>...</field2></two>"#).unwrap());
547+
//! # assert_eq!(AnyName::Text, quick_xml::de::from_str(r#"text"#).unwrap());
548+
//! # // TODO: After #474 parse mixed content
529549
//! ```
530550
//! ```
531551
//! # use pretty_assertions::assert_eq;
@@ -535,12 +555,14 @@
535555
//! #[serde(rename_all = "snake_case")]
536556
//! enum AnyName {
537557
//! One,
538-
//! // the <two> will be mapped to this
558+
//! // the <two> and textual content will be mapped to this
539559
//! #[serde(other)]
540560
//! Other,
541561
//! }
542562
//! # assert_eq!(AnyName::One, quick_xml::de::from_str(r#"<one field1="...">...</one>"#).unwrap());
543563
//! # assert_eq!(AnyName::Other, quick_xml::de::from_str(r#"<two><field2>...</field2></two>"#).unwrap());
564+
//! # assert_eq!(AnyName::Other, quick_xml::de::from_str(r#"text"#).unwrap());
565+
//! # // TODO: After #474 parse mixed content
544566
//! ```
545567
//! <div style="background:rgba(120,145,255,0.45);padding:0.75em;">
546568
//!
@@ -569,11 +591,19 @@
569591
//! <two>...</two>
570592
//! </any-tag>
571593
//! ```
594+
//! ```xml
595+
//! <any-tag field="...">
596+
//! Text <![CDATA[or (mixed)
597+
//! CDATA]]> content
598+
//! </any-tag>
599+
//! ```
572600
//! </td>
573601
//! <td>
574602
//!
575603
//! A structure with a field which type is an `enum`.
576604
//!
605+
//! If you need to get a textual content, mark a variant with `#[serde(rename = "$text")]`.
606+
//!
577607
//! Names of the enum, struct, and struct field with `Choice` type does not matter:
578608
//!
579609
//! ```
@@ -586,6 +616,14 @@
586616
//! enum Choice {
587617
//! One,
588618
//! Two,
619+
//!
620+
//! /// Use unit variant, if you do not care of a content.
621+
//! /// You can use tuple variant if you want to parse
622+
//! /// textual content as an xs:list.
623+
//! /// Struct variants are not supported and will return
624+
//! /// Err(Unsupported)
625+
//! #[serde(rename = "$text")]
626+
//! Text(String),
589627
//! }
590628
//! # #[derive(Debug, PartialEq)]
591629
//! #[derive(Deserialize)]
@@ -604,6 +642,11 @@
604642
//! # AnyName { field: (), any_name: Choice::Two },
605643
//! # quick_xml::de::from_str(r#"<any-tag field="..."><two>...</two></any-tag>"#).unwrap(),
606644
//! # );
645+
//! # assert_eq!(
646+
//! # AnyName { field: (), any_name: Choice::Text("text".into()) },
647+
//! # // TODO: After #474 parse mixed content
648+
//! # quick_xml::de::from_str(r#"<any-tag field="...">text</any-tag>"#).unwrap(),
649+
//! # );
607650
//! ```
608651
//! </td>
609652
//! </tr>
@@ -1023,8 +1066,7 @@
10231066
//! # );
10241067
//! ```
10251068
//! ```ignore
1026-
//! // FIXME: Custom("unknown variant `text`, expected
1027-
//! // one of `one`, `two`, `$value`")
1069+
//! // FIXME: #474
10281070
//! # use pretty_assertions::assert_eq;
10291071
//! # use serde::Deserialize;
10301072
//! # #[derive(Debug, PartialEq)]
@@ -1033,7 +1075,7 @@
10331075
//! enum Choice {
10341076
//! One,
10351077
//! Two,
1036-
//! #[serde(rename = "$value")]
1078+
//! #[serde(rename = "$text")]
10371079
//! Other(String),
10381080
//! }
10391081
//! type AnyName = Vec<Choice>;
@@ -1202,8 +1244,7 @@
12021244
//! You MUST specify `#[serde(rename = "$value")]` on that field:
12031245
//!
12041246
//! ```ignore
1205-
//! // FIXME: Custom("unknown variant `text`, expected
1206-
//! // one of `one`, `two`, `$value`")
1247+
//! // FIXME: #474
12071248
//! # use pretty_assertions::assert_eq;
12081249
//! # use serde::Deserialize;
12091250
//! # #[derive(Debug, PartialEq)]
@@ -1212,7 +1253,7 @@
12121253
//! enum Choice {
12131254
//! One,
12141255
//! Two,
1215-
//! #[serde(rename = "$value")]
1256+
//! #[serde(rename = "$text")]
12161257
//! Other(String),
12171258
//! }
12181259
//! # #[derive(Debug, PartialEq)]
@@ -1248,8 +1289,7 @@
12481289
//! # );
12491290
//! ```
12501291
//! ```ignore
1251-
//! // FIXME: Custom("unknown variant `text`, expected
1252-
//! // one of `one`, `two`, `$value`")
1292+
//! // FIXME: #474
12531293
//! # use pretty_assertions::assert_eq;
12541294
//! # use serde::Deserialize;
12551295
//! # #[derive(Debug, PartialEq)]
@@ -1258,7 +1298,7 @@
12581298
//! enum Choice {
12591299
//! One,
12601300
//! Two,
1261-
//! #[serde(rename = "$value")]
1301+
//! #[serde(rename = "$text")]
12621302
//! Other(String),
12631303
//! }
12641304
//! # #[derive(Debug, PartialEq)]
@@ -2433,8 +2473,7 @@ where
24332473
where
24342474
V: Visitor<'de>,
24352475
{
2436-
let value = visitor.visit_enum(var::EnumAccess::new(self))?;
2437-
Ok(value)
2476+
visitor.visit_enum(var::EnumAccess::new(self))
24382477
}
24392478

24402479
fn deserialize_seq<V>(self, visitor: V) -> Result<V::Value, DeError>

src/de/var.rs

Lines changed: 72 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
use crate::{
2-
de::{deserialize_bool, DeEvent, Deserializer, XmlRead},
2+
de::simple_type::SimpleTypeDeserializer,
3+
de::{deserialize_bool, DeEvent, Deserializer, XmlRead, TEXT_KEY},
34
encoding::Decoder,
45
errors::serialize::DeError,
56
escape::unescape,
67
};
7-
use serde::de::{self, DeserializeSeed, Deserializer as SerdeDeserializer, Visitor};
8+
use serde::de::value::StrDeserializer;
9+
use serde::de::{self, DeserializeSeed, Deserializer as _, Visitor};
810
use serde::{forward_to_deserialize_any, serde_if_integer128};
911
use std::borrow::Cow;
1012

@@ -37,21 +39,29 @@ where
3739
V: DeserializeSeed<'de>,
3840
{
3941
let decoder = self.de.reader.decoder();
40-
let de = match self.de.peek()? {
41-
DeEvent::Text(t) => VariantDeserializer::new(t.as_ref(), decoder, true),
42-
// Escape sequences does not processed inside CDATA section
43-
DeEvent::CData(t) => VariantDeserializer::new(t.as_ref(), decoder, false),
44-
DeEvent::Start(e) => {
45-
VariantDeserializer::new(e.local_name().into_inner(), decoder, false)
46-
}
47-
_ => {
48-
return Err(DeError::Unsupported(
49-
"Invalid event for Enum, expecting `Text` or `Start`".into(),
50-
))
51-
}
42+
let (name, is_text) = match self.de.peek()? {
43+
DeEvent::Start(e) => (
44+
seed.deserialize(VariantDeserializer::new(
45+
e.local_name().into_inner(),
46+
decoder,
47+
false,
48+
))?,
49+
false,
50+
),
51+
DeEvent::Text(_) | DeEvent::CData(_) => (
52+
seed.deserialize(StrDeserializer::<DeError>::new(TEXT_KEY))?,
53+
true,
54+
),
55+
DeEvent::End(e) => return Err(DeError::UnexpectedEnd(e.name().into_inner().to_vec())),
56+
DeEvent::Eof => return Err(DeError::UnexpectedEof),
5257
};
53-
let name = seed.deserialize(de)?;
54-
Ok((name, VariantAccess { de: self.de }))
58+
Ok((
59+
name,
60+
VariantAccess {
61+
de: self.de,
62+
is_text,
63+
},
64+
))
5565
}
5666
}
5767

@@ -60,6 +70,9 @@ where
6070
R: XmlRead<'de>,
6171
{
6272
de: &'a mut Deserializer<'de, R>,
73+
/// `true` if variant should be deserialized from a textual content
74+
/// and `false` if from tag
75+
is_text: bool,
6376
}
6477

6578
impl<'de, 'a, R> de::VariantAccess<'de> for VariantAccess<'de, 'a, R>
@@ -70,24 +83,52 @@ where
7083

7184
fn unit_variant(self) -> Result<(), DeError> {
7285
match self.de.next()? {
86+
// Consume subtree
7387
DeEvent::Start(e) => self.de.read_to_end(e.name()),
88+
// Does not needed to deserialize using SimpleTypeDeserializer, because
89+
// it returns `()` when `deserialize_unit()` is requested
7490
DeEvent::Text(_) | DeEvent::CData(_) => Ok(()),
75-
_ => unreachable!(),
91+
// SAFETY: the other events are filtered in `variant_seed()`
92+
_ => unreachable!("Only `Start`, `Text` or `CData` events are possible here"),
7693
}
7794
}
7895

7996
fn newtype_variant_seed<T>(self, seed: T) -> Result<T::Value, DeError>
8097
where
8198
T: DeserializeSeed<'de>,
8299
{
83-
seed.deserialize(&mut *self.de)
100+
if self.is_text {
101+
match self.de.next()? {
102+
DeEvent::Text(e) => {
103+
seed.deserialize(SimpleTypeDeserializer::from_text_content(e.decode(true)?))
104+
}
105+
DeEvent::CData(e) => {
106+
seed.deserialize(SimpleTypeDeserializer::from_text_content(e.decode()?))
107+
}
108+
// SAFETY: the other events are filtered in `variant_seed()`
109+
_ => unreachable!("Only `Text` or `CData` events are possible here"),
110+
}
111+
} else {
112+
seed.deserialize(&mut *self.de)
113+
}
84114
}
85115

86116
fn tuple_variant<V>(self, len: usize, visitor: V) -> Result<V::Value, DeError>
87117
where
88118
V: Visitor<'de>,
89119
{
90-
self.de.deserialize_tuple(len, visitor)
120+
if self.is_text {
121+
match self.de.next()? {
122+
DeEvent::Text(e) => SimpleTypeDeserializer::from_text_content(e.decode(true)?)
123+
.deserialize_tuple(len, visitor),
124+
DeEvent::CData(e) => SimpleTypeDeserializer::from_text_content(e.decode()?)
125+
.deserialize_tuple(len, visitor),
126+
// SAFETY: the other events are filtered in `variant_seed()`
127+
_ => unreachable!("Only `Text` or `CData` events are possible here"),
128+
}
129+
} else {
130+
self.de.deserialize_tuple(len, visitor)
131+
}
91132
}
92133

93134
fn struct_variant<V>(
@@ -98,7 +139,18 @@ where
98139
where
99140
V: Visitor<'de>,
100141
{
101-
self.de.deserialize_struct("", fields, visitor)
142+
if self.is_text {
143+
match self.de.next()? {
144+
DeEvent::Text(e) => SimpleTypeDeserializer::from_text_content(e.decode(true)?)
145+
.deserialize_struct("", fields, visitor),
146+
DeEvent::CData(e) => SimpleTypeDeserializer::from_text_content(e.decode()?)
147+
.deserialize_struct("", fields, visitor),
148+
// SAFETY: the other events are filtered in `variant_seed()`
149+
_ => unreachable!("Only `Text` or `CData` events are possible here"),
150+
}
151+
} else {
152+
self.de.deserialize_struct("", fields, visitor)
153+
}
102154
}
103155
}
104156

0 commit comments

Comments
 (0)