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/// // #[serde(tag = "@tag")]
189/// enum InternallyTaggedEnum {
190///     NewType(Newtype),
191///     Other,
192/// }
193///
194/// #[derive(Deserialize, Debug, PartialEq)]
195/// struct Newtype {
196///     #[serde(rename = "@attribute")]
197///     attribute: u64,
198/// }
199///
200/// // The macro needs the type of the enum, the tag name,
201/// // and information about all the variants
202/// impl_deserialize_for_internally_tagged_enum!{
203///     InternallyTaggedEnum, "@tag",
204///     ("NewType" => NewType(Newtype)),
205///     (_ => Other),
206/// }
207///
208/// assert_eq!(
209///     from_str::<Root>(r#"
210///         <root>
211///             <one tag="NewType" attribute="42" />
212///             <two tag="Something" ignoredAttribute="something" />
213///             <three tag="SomethingElse">
214///                 <ignoredToo />
215///             </three>
216///         </root>
217///     "#).unwrap(),
218///     Root {
219///         one: InternallyTaggedEnum::NewType(Newtype { attribute: 42 }),
220///         two: InternallyTaggedEnum::Other,
221///         three: InternallyTaggedEnum::Other,
222///     },
223/// );
224/// ```
225///
226/// If some struct or newtype variants have the specially named `$text` or `$value` fields,
227/// you need to say that to the generated `Deserialize` implementation. XML deserializer
228/// uses presence of that fields to determine if it need to emit such keys. You may specify,
229/// which keys should be available in square brackets just after the tag name and colon:
230///
231/// ```
232/// # use pretty_assertions::assert_eq;
233/// use quick_xml::de::from_str;
234/// use quick_xml::impl_deserialize_for_internally_tagged_enum;
235/// use serde::Deserialize;
236///
237/// #[derive(Deserialize, Debug, PartialEq)]
238/// struct Root {
239///     one: InternallyTaggedEnum,
240///     two: InternallyTaggedEnum,
241/// }
242///
243/// #[derive(Deserialize, Debug, PartialEq)]
244/// enum ExternallyTaggedEnum {
245///     First,
246///     Second,
247/// }
248/// #[derive(Debug, PartialEq)]
249/// // #[serde(tag = "@tag")]
250/// enum InternallyTaggedEnum {
251///     NewType(Newtype),
252///     Struct {
253///         // #[serde(rename = "$value")]
254///         any: ExternallyTaggedEnum,
255///     },
256/// }
257///
258/// #[derive(Deserialize, Debug, PartialEq)]
259/// struct Newtype {
260///     #[serde(rename = "$value")]
261///     any: ExternallyTaggedEnum,
262/// }
263///
264/// // The macro needs the type of the enum, the tag name, the list of fields,
265/// // and information about all the variants
266/// impl_deserialize_for_internally_tagged_enum!{
267///     // Without "$value" you get
268///     // called `Result::unwrap()` on an `Err` value: Custom("missing field `$value`")
269///     // That list will be passed to `deserialize_struct`
270///     InternallyTaggedEnum, "@tag": ["$value"],
271///
272///     ("NewType" => NewType(Newtype)),
273///     ("Struct" => Struct {
274///         #[serde(rename = "$value")]
275///         any: ExternallyTaggedEnum,
276///     }),
277/// }
278///
279/// assert_eq!(
280///     from_str::<Root>(r#"
281///         <root>
282///             <one tag="NewType">
283///                 <First />
284///             </one>
285///             <two tag="Struct">
286///                 <Second />
287///             </two>
288///         </root>
289///     "#).unwrap(),
290///     Root {
291///         one: InternallyTaggedEnum::NewType(Newtype { any: ExternallyTaggedEnum::First }),
292///         two: InternallyTaggedEnum::Struct { any: ExternallyTaggedEnum::Second },
293///     },
294/// );
295/// ```
296/// <div style="background:rgba(120,145,255,0.45);padding:0.75em;">
297///
298/// NOTE: In addition to `$value` you must specify _all_ field names that may appear
299/// in every variant! Otherwise you'll get ```Custom("missing field `unlisted field name`")```
300/// error. That is because XML tags for all unlisted field names would be mapped to field `$value`.
301/// If your types do not have a `$value` special field, you may omit the field list.
302///
303/// </div>
304///
305/// [internally tagged]: https://serde.rs/enum-representations.html#internally-tagged
306/// [serde#1183]: https://github.com/serde-rs/serde/issues/1183
307#[macro_export(local_inner_macros)]
308macro_rules! impl_deserialize_for_internally_tagged_enum {
309    (
310        $enum:ty,
311        $tag:literal $(:[ $($field:literal),* ])?,
312        $($cases:tt)*
313    ) => {
314        impl<'de> serde::de::Deserialize<'de> for $enum {
315            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
316            where
317                D: serde::de::Deserializer<'de>,
318            {
319                use serde::de::{Error, MapAccess, Visitor};
320
321                // The Visitor struct is normally used for state, but none is needed
322                struct TheVisitor;
323                // The main logic of the deserializing happens in the Visitor trait
324                impl<'de> Visitor<'de> for TheVisitor {
325                    // The type that is being deserialized
326                    type Value = $enum;
327
328                    // Try to give a better error message when this is used wrong
329                    fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
330                        f.write_str("expecting map with tag in ")?;
331                        f.write_str($tag)
332                    }
333
334                    // The xml data is provided as an opaque map,
335                    // that map is parsed into the type
336                    fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
337                    where
338                        A: MapAccess<'de>,
339                    {
340                        // Here the assumption is made that only one attribute
341                        // exists and it's the discriminator (enum "tag").
342                        let entry: Option<(String, String)> = map.next_entry()?;
343                        // If there are more attributes those would need
344                        // to be parsed as well.
345                        let tag = match entry {
346                            // Return an error if the no attributes are found,
347                            // and indicate that the @tag attribute is missing.
348                            None => Err(A::Error::missing_field($tag)),
349                            // Check if the attribute is the tag
350                            Some((attribute, value)) => {
351                                if attribute == $tag {
352                                    // return the value of the tag
353                                    Ok(value)
354                                } else {
355                                    // The attribute is not @tag, return an error
356                                    // indicating that there is an unexpected attribute
357                                    Err(A::Error::unknown_field(&attribute, &[$tag]))
358                                }
359                            }
360                        }?;
361
362                        let de = serde::de::value::MapAccessDeserializer::new(map);
363                        $crate::deserialize_match!( tag, de, $enum, $($cases)* )
364                    }
365                }
366                // Tell the deserializer to deserialize the data as a map,
367                // using the TheVisitor as the decoder
368                deserializer.deserialize_struct(std::stringify!($enum), &[ $($($field),*)? ], TheVisitor)
369            }
370        }
371    }
372}
373
374/// Provides helper functions to serialization and deserialization of types
375/// (usually enums) as a text content of an element and intended to use with
376/// [`#[serde(with = "...")]`][with], [`#[serde(deserialize_with = "...")]`][de-with]
377/// and [`#[serde(serialize_with = "...")]`][se-with].
378///
379/// ```
380/// # use pretty_assertions::assert_eq;
381/// use quick_xml::de::from_str;
382/// use quick_xml::se::to_string;
383/// use serde::{Serialize, Deserialize};
384///
385/// #[derive(Serialize, Deserialize, PartialEq, Debug)]
386/// enum SomeEnum {
387///     // Default implementation serializes enum as an `<EnumValue/>` element
388///     EnumValue,
389/// # /*
390///     ...
391/// # */
392/// }
393///
394/// #[derive(Serialize, Deserialize, PartialEq, Debug)]
395/// #[serde(rename = "some-container")]
396/// struct SomeContainer {
397///     #[serde(with = "quick_xml::serde_helpers::text_content")]
398///     field: SomeEnum,
399/// }
400///
401/// let container = SomeContainer {
402///     field: SomeEnum::EnumValue,
403/// };
404/// let xml = "\
405///     <some-container>\
406///         <field>EnumValue</field>\
407///     </some-container>";
408///
409/// assert_eq!(to_string(&container).unwrap(), xml);
410/// assert_eq!(from_str::<SomeContainer>(xml).unwrap(), container);
411/// ```
412///
413/// Using of this module is equivalent to replacing `field`'s type to this:
414///
415/// ```
416/// # use serde::{Deserialize, Serialize};
417/// # type SomeEnum = ();
418/// #[derive(Serialize, Deserialize)]
419/// struct Field {
420///     // Use a special name `$text` to map field to the text content
421///     #[serde(rename = "$text")]
422///     content: SomeEnum,
423/// }
424///
425/// #[derive(Serialize, Deserialize)]
426/// #[serde(rename = "some-container")]
427/// struct SomeContainer {
428///     field: Field,
429/// }
430/// ```
431/// Read about the meaning of a special [`$text`] field.
432///
433/// In versions of quick-xml before 0.31.0 this module used to represent enum
434/// unit variants as `<field>EnumUnitVariant</field>` instead of `<EnumUnitVariant/>`.
435/// Since version 0.31.0 this is default representation of enums in normal fields,
436/// and `<EnumUnitVariant/>` requires `$value` field:
437///
438/// ```
439/// # use pretty_assertions::assert_eq;
440/// use quick_xml::de::from_str;
441/// use quick_xml::se::to_string;
442/// use serde::{Serialize, Deserialize};
443///
444/// #[derive(Serialize, Deserialize, PartialEq, Debug)]
445/// enum SomeEnum {
446///     // Default implementation serializes enum as an `<EnumValue/>` element
447///     EnumValue,
448/// # /*
449///     ...
450/// # */
451/// }
452///
453/// #[derive(Serialize, Deserialize, PartialEq, Debug)]
454/// #[serde(rename = "some-container")]
455/// struct SomeContainer {
456///     #[serde(rename = "$value")]
457///     field: SomeEnum,
458/// }
459///
460/// let container = SomeContainer {
461///     field: SomeEnum::EnumValue,
462/// };
463/// let xml = "\
464///     <some-container>\
465///         <EnumValue/>\
466///     </some-container>";
467///
468/// assert_eq!(to_string(&container).unwrap(), xml);
469/// assert_eq!(from_str::<SomeContainer>(xml).unwrap(), container);
470/// ```
471///
472/// [with]: https://serde.rs/field-attrs.html#with
473/// [de-with]: https://serde.rs/field-attrs.html#deserialize_with
474/// [se-with]: https://serde.rs/field-attrs.html#serialize_with
475/// [`$text`]: ../../de/index.html#text
476pub mod text_content {
477    use super::*;
478
479    /// Serializes `value` as an XSD [simple type]. Intended to use with
480    /// `#[serde(serialize_with = "...")]`. See example at [`text_content`]
481    /// module level.
482    ///
483    /// [simple type]: https://www.w3.org/TR/xmlschema11-1/#Simple_Type_Definition
484    pub fn serialize<S, T>(value: &T, serializer: S) -> Result<S::Ok, S::Error>
485    where
486        S: Serializer,
487        T: Serialize,
488    {
489        #[derive(Serialize)]
490        struct Field<'a, T> {
491            #[serde(rename = "$text")]
492            value: &'a T,
493        }
494        Field { value }.serialize(serializer)
495    }
496
497    /// Deserializes XSD's [simple type]. Intended to use with
498    /// `#[serde(deserialize_with = "...")]`. See example at [`text_content`]
499    /// module level.
500    ///
501    /// [simple type]: https://www.w3.org/TR/xmlschema11-1/#Simple_Type_Definition
502    pub fn deserialize<'de, D, T>(deserializer: D) -> Result<T, D::Error>
503    where
504        D: Deserializer<'de>,
505        T: Deserialize<'de>,
506    {
507        #[derive(Deserialize)]
508        struct Field<T> {
509            #[serde(rename = "$text")]
510            value: T,
511        }
512        Ok(Field::deserialize(deserializer)?.value)
513    }
514}