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/// A helper to implement [`Deserialize`] for [internally tagged] enums which
48/// does not use [`Deserializer::deserialize_any`] that produces wrong results
49/// with XML because of [serde#1183].
50///
51/// In contrast to deriving [`Deserialize`] this macro assumes that a tag will be
52/// the first element or attribute in the XML.
53///
54/// # Example
55///
56/// ```
57/// # use pretty_assertions::assert_eq;
58/// use quick_xml::de::from_str;
59/// use quick_xml::impl_deserialize_for_internally_tagged_enum;
60/// use serde::Deserialize;
61///
62/// #[derive(Deserialize, Debug, PartialEq)]
63/// struct Root {
64///     one: InternallyTaggedEnum,
65///     two: InternallyTaggedEnum,
66///     three: InternallyTaggedEnum,
67/// }
68///
69/// #[derive(Debug, PartialEq)]
70/// // #[serde(tag = "@tag")]
71/// enum InternallyTaggedEnum {
72///     Unit,
73///     Newtype(Newtype),
74///     Struct {
75///         // #[serde(rename = "@attribute")]
76///         attribute: u32,
77///         element: f32,
78///     },
79/// }
80///
81/// #[derive(Deserialize, Debug, PartialEq)]
82/// struct Newtype {
83///     #[serde(rename = "@attribute")]
84///     attribute: u64,
85/// }
86///
87/// // The macro needs the type of the enum, the tag name,
88/// // and information about all the variants
89/// impl_deserialize_for_internally_tagged_enum!{
90///     InternallyTaggedEnum, "@tag",
91///     ("Unit"    => Unit),
92///     ("Newtype" => Newtype(Newtype)),
93///     ("Struct"  => Struct {
94///         #[serde(rename = "@attribute")]
95///         attribute: u32,
96///         element: f32,
97///     }),
98/// }
99///
100/// assert_eq!(
101///     from_str::<Root>(r#"
102///         <root>
103///             <one tag="Unit" />
104///             <two tag="Newtype" attribute="42" />
105///             <three tag="Struct" attribute="42">
106///                 <element>4.2</element>
107///             </three>
108///         </root>
109///     "#).unwrap(),
110///     Root {
111///         one: InternallyTaggedEnum::Unit,
112///         two: InternallyTaggedEnum::Newtype(Newtype { attribute: 42 }),
113///         three: InternallyTaggedEnum::Struct {
114///             attribute: 42,
115///             element: 4.2,
116///         },
117///     },
118/// );
119/// ```
120///
121/// [internally tagged]: https://serde.rs/enum-representations.html#internally-tagged
122/// [serde#1183]: https://github.com/serde-rs/serde/issues/1183
123#[macro_export(local_inner_macros)]
124macro_rules! impl_deserialize_for_internally_tagged_enum {
125    (
126        $enum:ty,
127        $tag:literal,
128        $(
129            ($variant_tag:literal => $($variant:tt)+ )
130        ),* $(,)?
131    ) => {
132        impl<'de> serde::de::Deserialize<'de> for $enum {
133            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
134            where
135                D: serde::de::Deserializer<'de>,
136            {
137                use serde::de::{Error, MapAccess, Visitor};
138
139                // The Visitor struct is normally used for state, but none is needed
140                struct TheVisitor;
141                // The main logic of the deserializing happens in the Visitor trait
142                impl<'de> Visitor<'de> for TheVisitor {
143                    // The type that is being deserialized
144                    type Value = $enum;
145
146                    // Try to give a better error message when this is used wrong
147                    fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
148                        f.write_str("expecting map with tag in ")?;
149                        f.write_str($tag)
150                    }
151
152                    // The xml data is provided as an opaque map,
153                    // that map is parsed into the type
154                    fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
155                    where
156                        A: MapAccess<'de>,
157                    {
158                        // Here the assumption is made that only one attribute
159                        // exists and it's the discriminator (enum "tag").
160                        let entry: Option<(String, String)> = map.next_entry()?;
161                        // If there are more attributes those would need
162                        // to be parsed as well.
163                        let tag = match entry {
164                            // Return an error if the no attributes are found,
165                            // and indicate that the @tag attribute is missing.
166                            None => Err(A::Error::missing_field($tag)),
167                            // Check if the attribute is the tag
168                            Some((attribute, value)) => {
169                                if attribute == $tag {
170                                    // return the value of the tag
171                                    Ok(value)
172                                } else {
173                                    // The attribute is not @tag, return an error
174                                    // indicating that there is an unexpected attribute
175                                    Err(A::Error::unknown_field(&attribute, &[$tag]))
176                                }
177                            }
178                        }?;
179
180                        let de = serde::de::value::MapAccessDeserializer::new(map);
181                        match tag.as_ref() {
182                            $(
183                                $variant_tag => Ok(deserialize_variant!( de, $enum, $($variant)+ )),
184                            )*
185                            _ => Err(A::Error::unknown_field(&tag, &[$($variant_tag),+])),
186                        }
187                    }
188                }
189                // Tell the deserializer to deserialize the data as a map,
190                // using the TheVisitor as the decoder
191                deserializer.deserialize_map(TheVisitor)
192            }
193        }
194    }
195}
196
197/// Provides helper functions to serialization and deserialization of types
198/// (usually enums) as a text content of an element and intended to use with
199/// [`#[serde(with = "...")]`][with], [`#[serde(deserialize_with = "...")]`][de-with]
200/// and [`#[serde(serialize_with = "...")]`][se-with].
201///
202/// ```
203/// # use pretty_assertions::assert_eq;
204/// use quick_xml::de::from_str;
205/// use quick_xml::se::to_string;
206/// use serde::{Serialize, Deserialize};
207///
208/// #[derive(Serialize, Deserialize, PartialEq, Debug)]
209/// enum SomeEnum {
210///     // Default implementation serializes enum as an `<EnumValue/>` element
211///     EnumValue,
212/// # /*
213///     ...
214/// # */
215/// }
216///
217/// #[derive(Serialize, Deserialize, PartialEq, Debug)]
218/// #[serde(rename = "some-container")]
219/// struct SomeContainer {
220///     #[serde(with = "quick_xml::serde_helpers::text_content")]
221///     field: SomeEnum,
222/// }
223///
224/// let container = SomeContainer {
225///     field: SomeEnum::EnumValue,
226/// };
227/// let xml = "\
228///     <some-container>\
229///         <field>EnumValue</field>\
230///     </some-container>";
231///
232/// assert_eq!(to_string(&container).unwrap(), xml);
233/// assert_eq!(from_str::<SomeContainer>(xml).unwrap(), container);
234/// ```
235///
236/// Using of this module is equivalent to replacing `field`'s type to this:
237///
238/// ```
239/// # use serde::{Deserialize, Serialize};
240/// # type SomeEnum = ();
241/// #[derive(Serialize, Deserialize)]
242/// struct Field {
243///     // Use a special name `$text` to map field to the text content
244///     #[serde(rename = "$text")]
245///     content: SomeEnum,
246/// }
247///
248/// #[derive(Serialize, Deserialize)]
249/// #[serde(rename = "some-container")]
250/// struct SomeContainer {
251///     field: Field,
252/// }
253/// ```
254/// Read about the meaning of a special [`$text`] field.
255///
256/// In versions of quick-xml before 0.31.0 this module used to represent enum
257/// unit variants as `<field>EnumUnitVariant</field>` instead of `<EnumUnitVariant/>`.
258/// Since version 0.31.0 this is default representation of enums in normal fields,
259/// and `<EnumUnitVariant/>` requires `$value` field:
260///
261/// ```
262/// # use pretty_assertions::assert_eq;
263/// use quick_xml::de::from_str;
264/// use quick_xml::se::to_string;
265/// use serde::{Serialize, Deserialize};
266///
267/// #[derive(Serialize, Deserialize, PartialEq, Debug)]
268/// enum SomeEnum {
269///     // Default implementation serializes enum as an `<EnumValue/>` element
270///     EnumValue,
271/// # /*
272///     ...
273/// # */
274/// }
275///
276/// #[derive(Serialize, Deserialize, PartialEq, Debug)]
277/// #[serde(rename = "some-container")]
278/// struct SomeContainer {
279///     #[serde(rename = "$value")]
280///     field: SomeEnum,
281/// }
282///
283/// let container = SomeContainer {
284///     field: SomeEnum::EnumValue,
285/// };
286/// let xml = "\
287///     <some-container>\
288///         <EnumValue/>\
289///     </some-container>";
290///
291/// assert_eq!(to_string(&container).unwrap(), xml);
292/// assert_eq!(from_str::<SomeContainer>(xml).unwrap(), container);
293/// ```
294///
295/// [with]: https://serde.rs/field-attrs.html#with
296/// [de-with]: https://serde.rs/field-attrs.html#deserialize_with
297/// [se-with]: https://serde.rs/field-attrs.html#serialize_with
298/// [`$text`]: ../../de/index.html#text
299pub mod text_content {
300    use super::*;
301
302    /// Serializes `value` as an XSD [simple type]. Intended to use with
303    /// `#[serde(serialize_with = "...")]`. See example at [`text_content`]
304    /// module level.
305    ///
306    /// [simple type]: https://www.w3.org/TR/xmlschema11-1/#Simple_Type_Definition
307    pub fn serialize<S, T>(value: &T, serializer: S) -> Result<S::Ok, S::Error>
308    where
309        S: Serializer,
310        T: Serialize,
311    {
312        #[derive(Serialize)]
313        struct Field<'a, T> {
314            #[serde(rename = "$text")]
315            value: &'a T,
316        }
317        Field { value }.serialize(serializer)
318    }
319
320    /// Deserializes XSD's [simple type]. Intended to use with
321    /// `#[serde(deserialize_with = "...")]`. See example at [`text_content`]
322    /// module level.
323    ///
324    /// [simple type]: https://www.w3.org/TR/xmlschema11-1/#Simple_Type_Definition
325    pub fn deserialize<'de, D, T>(deserializer: D) -> Result<T, D::Error>
326    where
327        D: Deserializer<'de>,
328        T: Deserialize<'de>,
329    {
330        #[derive(Deserialize)]
331        struct Field<T> {
332            #[serde(rename = "$text")]
333            value: T,
334        }
335        Ok(Field::deserialize(deserializer)?.value)
336    }
337}