quick_xml/serde_helpers.rs
1//! Provides helper functions to glue an XML with a serde content model.
2
3use serde::{Deserialize, Deserializer, Serialize, Serializer};
4
5#[macro_export]
6#[doc(hidden)]
7macro_rules! deserialize_variant {
8 // Produce struct enum variant
9 ( $de:expr, $enum:tt, $variant:ident {
10 $(
11 $(#[$meta:meta])*
12 $field:ident : $typ:ty
13 ),* $(,)?
14 } ) => ({
15 let var = {
16 // Create anonymous type
17 #[derive(serde::Deserialize)]
18 struct $variant {
19 $(
20 $(#[$meta])*
21 $field: $typ,
22 )*
23 }
24 <$variant>::deserialize($de)?
25 };
26 // Due to https://github.com/rust-lang/rust/issues/86935 we cannot use
27 // <$enum> :: $variant
28 use $enum :: *;
29 $variant {
30 $($field: var.$field,)*
31 }
32 });
33
34 // Produce newtype enum variant
35 ( $de:expr, $enum:tt, $variant:ident($typ:ty) ) => ({
36 let var = <$typ>::deserialize($de)?;
37 <$enum> :: $variant(var)
38 });
39
40 // Produce unit enum variant
41 ( $de:expr, $enum:tt, $variant:ident ) => ({
42 serde::de::IgnoredAny::deserialize($de)?;
43 <$enum> :: $variant
44 });
45}
46
47/// Helper macro that generates different match expressions depending on the presence
48/// of default variant
49#[macro_export]
50#[doc(hidden)]
51macro_rules! deserialize_match {
52 // Only default variant
53 (
54 $tag:ident, $de:ident, $enum:ty,
55 (_ => $($default_variant:tt)+ )
56 $(,)?
57 ) => (
58 Ok($crate::deserialize_variant!( $de, $enum, $($default_variant)+ ))
59 );
60
61 // With default variant
62 (
63 $tag:ident, $de:ident, $enum:ty,
64 $(
65 ($variant_tag:literal => $($variant:tt)+ )
66 ),*
67 , (_ => $($default_variant:tt)+ )
68 $(,)?
69 ) => (
70 match $tag.as_ref() {
71 $(
72 $variant_tag => Ok($crate::deserialize_variant!( $de, $enum, $($variant)+ )),
73 )*
74 _ => Ok($crate::deserialize_variant!( $de, $enum, $($default_variant)+ )),
75 }
76 );
77
78 // Without default variant
79 (
80 $tag:ident, $de:ident, $enum:ty,
81 $(
82 ($variant_tag:literal => $($variant:tt)+ )
83 ),*
84 $(,)?
85 ) => (
86 match $tag.as_ref() {
87 $(
88 $variant_tag => Ok($crate::deserialize_variant!( $de, $enum, $($variant)+ )),
89 )*
90 _ => Err(A::Error::unknown_field(&$tag, &[$($variant_tag),+])),
91 }
92 );
93}
94
95/// A helper to implement [`Deserialize`] for [internally tagged] enums which
96/// does not use [`Deserializer::deserialize_any`] that produces wrong results
97/// with XML because of [serde#1183].
98///
99/// In contrast to deriving [`Deserialize`] this macro assumes that a tag will be
100/// the first element or attribute in the XML.
101///
102/// # Example
103///
104/// ```
105/// # use pretty_assertions::assert_eq;
106/// use quick_xml::de::from_str;
107/// use quick_xml::impl_deserialize_for_internally_tagged_enum;
108/// use serde::Deserialize;
109///
110/// #[derive(Deserialize, Debug, PartialEq)]
111/// struct Root {
112/// one: InternallyTaggedEnum,
113/// two: InternallyTaggedEnum,
114/// three: InternallyTaggedEnum,
115/// }
116///
117/// #[derive(Debug, PartialEq)]
118/// // #[serde(tag = "@tag")]
119/// enum InternallyTaggedEnum {
120/// Unit,
121/// Newtype(Newtype),
122/// Struct {
123/// // #[serde(rename = "@attribute")]
124/// attribute: u32,
125/// element: f32,
126/// },
127/// }
128///
129/// #[derive(Deserialize, Debug, PartialEq)]
130/// struct Newtype {
131/// #[serde(rename = "@attribute")]
132/// attribute: u64,
133/// }
134///
135/// // The macro needs the type of the enum, the tag name,
136/// // and information about all the variants
137/// impl_deserialize_for_internally_tagged_enum!{
138/// InternallyTaggedEnum, "@tag",
139/// ("Unit" => Unit),
140/// ("Newtype" => Newtype(Newtype)),
141/// ("Struct" => Struct {
142/// #[serde(rename = "@attribute")]
143/// attribute: u32,
144/// element: f32,
145/// }),
146/// }
147///
148/// assert_eq!(
149/// from_str::<Root>(r#"
150/// <root>
151/// <one tag="Unit" />
152/// <two tag="Newtype" attribute="42" />
153/// <three tag="Struct" attribute="42">
154/// <element>4.2</element>
155/// </three>
156/// </root>
157/// "#).unwrap(),
158/// Root {
159/// one: InternallyTaggedEnum::Unit,
160/// two: InternallyTaggedEnum::Newtype(Newtype { attribute: 42 }),
161/// three: InternallyTaggedEnum::Struct {
162/// attribute: 42,
163/// element: 4.2,
164/// },
165/// },
166/// );
167/// ```
168///
169/// You don't necessarily have to provide all the enumeration variants and can use
170/// `_` to put every undefined tag into an enumeration variant.
171/// This default variant (`_ => ...`) must be the last one to appear in the macro,
172/// like `_ => Other` in the example below:
173///
174/// ```
175/// # use pretty_assertions::assert_eq;
176/// use quick_xml::de::from_str;
177/// use quick_xml::impl_deserialize_for_internally_tagged_enum;
178/// use serde::Deserialize;
179///
180/// #[derive(Deserialize, Debug, PartialEq)]
181/// struct Root {
182/// one: InternallyTaggedEnum,
183/// two: InternallyTaggedEnum,
184/// three: InternallyTaggedEnum,
185/// }
186///
187/// #[derive(Debug, PartialEq)]
188/// enum InternallyTaggedEnum {
189/// NewType(Newtype),
190/// Other,
191/// }
192///
193/// #[derive(Deserialize, Debug, PartialEq)]
194/// struct Newtype {
195/// #[serde(rename = "@attribute")]
196/// attribute: u64,
197/// }
198///
199/// // The macro needs the type of the enum, the tag name,
200/// // and information about all the variants
201/// impl_deserialize_for_internally_tagged_enum!{
202/// InternallyTaggedEnum, "@tag",
203/// ("NewType" => NewType(Newtype)),
204/// (_ => Other),
205/// }
206///
207/// assert_eq!(
208/// from_str::<Root>(r#"
209/// <root>
210/// <one tag="NewType" attribute="42" />
211/// <two tag="Something" ignoredAttribute="something" />
212/// <three tag="SomethingElse">
213/// <ignoredToo />
214/// </three>
215/// </root>
216/// "#).unwrap(),
217/// Root {
218/// one: InternallyTaggedEnum::NewType(Newtype { attribute: 42 }),
219/// two: InternallyTaggedEnum::Other,
220/// three: InternallyTaggedEnum::Other,
221/// },
222/// );
223/// ```
224///
225/// [internally tagged]: https://serde.rs/enum-representations.html#internally-tagged
226/// [serde#1183]: https://github.com/serde-rs/serde/issues/1183
227#[macro_export(local_inner_macros)]
228macro_rules! impl_deserialize_for_internally_tagged_enum {
229 (
230 $enum:ty,
231 $tag:literal,
232 $($cases:tt)*
233 ) => {
234 impl<'de> serde::de::Deserialize<'de> for $enum {
235 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
236 where
237 D: serde::de::Deserializer<'de>,
238 {
239 use serde::de::{Error, MapAccess, Visitor};
240
241 // The Visitor struct is normally used for state, but none is needed
242 struct TheVisitor;
243 // The main logic of the deserializing happens in the Visitor trait
244 impl<'de> Visitor<'de> for TheVisitor {
245 // The type that is being deserialized
246 type Value = $enum;
247
248 // Try to give a better error message when this is used wrong
249 fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
250 f.write_str("expecting map with tag in ")?;
251 f.write_str($tag)
252 }
253
254 // The xml data is provided as an opaque map,
255 // that map is parsed into the type
256 fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
257 where
258 A: MapAccess<'de>,
259 {
260 // Here the assumption is made that only one attribute
261 // exists and it's the discriminator (enum "tag").
262 let entry: Option<(String, String)> = map.next_entry()?;
263 // If there are more attributes those would need
264 // to be parsed as well.
265 let tag = match entry {
266 // Return an error if the no attributes are found,
267 // and indicate that the @tag attribute is missing.
268 None => Err(A::Error::missing_field($tag)),
269 // Check if the attribute is the tag
270 Some((attribute, value)) => {
271 if attribute == $tag {
272 // return the value of the tag
273 Ok(value)
274 } else {
275 // The attribute is not @tag, return an error
276 // indicating that there is an unexpected attribute
277 Err(A::Error::unknown_field(&attribute, &[$tag]))
278 }
279 }
280 }?;
281
282 let de = serde::de::value::MapAccessDeserializer::new(map);
283 $crate::deserialize_match!( tag, de, $enum, $($cases)* )
284 }
285 }
286 // Tell the deserializer to deserialize the data as a map,
287 // using the TheVisitor as the decoder
288 deserializer.deserialize_map(TheVisitor)
289 }
290 }
291 }
292}
293
294/// Provides helper functions to serialization and deserialization of types
295/// (usually enums) as a text content of an element and intended to use with
296/// [`#[serde(with = "...")]`][with], [`#[serde(deserialize_with = "...")]`][de-with]
297/// and [`#[serde(serialize_with = "...")]`][se-with].
298///
299/// ```
300/// # use pretty_assertions::assert_eq;
301/// use quick_xml::de::from_str;
302/// use quick_xml::se::to_string;
303/// use serde::{Serialize, Deserialize};
304///
305/// #[derive(Serialize, Deserialize, PartialEq, Debug)]
306/// enum SomeEnum {
307/// // Default implementation serializes enum as an `<EnumValue/>` element
308/// EnumValue,
309/// # /*
310/// ...
311/// # */
312/// }
313///
314/// #[derive(Serialize, Deserialize, PartialEq, Debug)]
315/// #[serde(rename = "some-container")]
316/// struct SomeContainer {
317/// #[serde(with = "quick_xml::serde_helpers::text_content")]
318/// field: SomeEnum,
319/// }
320///
321/// let container = SomeContainer {
322/// field: SomeEnum::EnumValue,
323/// };
324/// let xml = "\
325/// <some-container>\
326/// <field>EnumValue</field>\
327/// </some-container>";
328///
329/// assert_eq!(to_string(&container).unwrap(), xml);
330/// assert_eq!(from_str::<SomeContainer>(xml).unwrap(), container);
331/// ```
332///
333/// Using of this module is equivalent to replacing `field`'s type to this:
334///
335/// ```
336/// # use serde::{Deserialize, Serialize};
337/// # type SomeEnum = ();
338/// #[derive(Serialize, Deserialize)]
339/// struct Field {
340/// // Use a special name `$text` to map field to the text content
341/// #[serde(rename = "$text")]
342/// content: SomeEnum,
343/// }
344///
345/// #[derive(Serialize, Deserialize)]
346/// #[serde(rename = "some-container")]
347/// struct SomeContainer {
348/// field: Field,
349/// }
350/// ```
351/// Read about the meaning of a special [`$text`] field.
352///
353/// In versions of quick-xml before 0.31.0 this module used to represent enum
354/// unit variants as `<field>EnumUnitVariant</field>` instead of `<EnumUnitVariant/>`.
355/// Since version 0.31.0 this is default representation of enums in normal fields,
356/// and `<EnumUnitVariant/>` requires `$value` field:
357///
358/// ```
359/// # use pretty_assertions::assert_eq;
360/// use quick_xml::de::from_str;
361/// use quick_xml::se::to_string;
362/// use serde::{Serialize, Deserialize};
363///
364/// #[derive(Serialize, Deserialize, PartialEq, Debug)]
365/// enum SomeEnum {
366/// // Default implementation serializes enum as an `<EnumValue/>` element
367/// EnumValue,
368/// # /*
369/// ...
370/// # */
371/// }
372///
373/// #[derive(Serialize, Deserialize, PartialEq, Debug)]
374/// #[serde(rename = "some-container")]
375/// struct SomeContainer {
376/// #[serde(rename = "$value")]
377/// field: SomeEnum,
378/// }
379///
380/// let container = SomeContainer {
381/// field: SomeEnum::EnumValue,
382/// };
383/// let xml = "\
384/// <some-container>\
385/// <EnumValue/>\
386/// </some-container>";
387///
388/// assert_eq!(to_string(&container).unwrap(), xml);
389/// assert_eq!(from_str::<SomeContainer>(xml).unwrap(), container);
390/// ```
391///
392/// [with]: https://serde.rs/field-attrs.html#with
393/// [de-with]: https://serde.rs/field-attrs.html#deserialize_with
394/// [se-with]: https://serde.rs/field-attrs.html#serialize_with
395/// [`$text`]: ../../de/index.html#text
396pub mod text_content {
397 use super::*;
398
399 /// Serializes `value` as an XSD [simple type]. Intended to use with
400 /// `#[serde(serialize_with = "...")]`. See example at [`text_content`]
401 /// module level.
402 ///
403 /// [simple type]: https://www.w3.org/TR/xmlschema11-1/#Simple_Type_Definition
404 pub fn serialize<S, T>(value: &T, serializer: S) -> Result<S::Ok, S::Error>
405 where
406 S: Serializer,
407 T: Serialize,
408 {
409 #[derive(Serialize)]
410 struct Field<'a, T> {
411 #[serde(rename = "$text")]
412 value: &'a T,
413 }
414 Field { value }.serialize(serializer)
415 }
416
417 /// Deserializes XSD's [simple type]. Intended to use with
418 /// `#[serde(deserialize_with = "...")]`. See example at [`text_content`]
419 /// module level.
420 ///
421 /// [simple type]: https://www.w3.org/TR/xmlschema11-1/#Simple_Type_Definition
422 pub fn deserialize<'de, D, T>(deserializer: D) -> Result<T, D::Error>
423 where
424 D: Deserializer<'de>,
425 T: Deserialize<'de>,
426 {
427 #[derive(Deserialize)]
428 struct Field<T> {
429 #[serde(rename = "$text")]
430 value: T,
431 }
432 Ok(Field::deserialize(deserializer)?.value)
433 }
434}