Repository: tafia/quick-xml
Pull request #661 (2023-10-06 17:22:40)

More strict deserialization into primitives
This PR restricts ability to deserialize primitive types (numbers, booleans, characters, strings) from XML documents that contains a trailing tag after a text. Previously the error emitted only when tag was before text.

Before that PR the XML
```xml
<struct>
  <field>42<something-wrong/></field>
</struct>
```
can be successfully deserialized into
```rust
struct Struct {
  field: u32,
}
```
which is not that you want generally. Now the `DeError::UnexpectedStart` error is returned. Also, probably we want that `42<something-wrong/>` was passed to the `u32::deserialize` and deserialization fail because type decide that (for example, we might want to capture tag in `String`), but this would be possible only when #257 would be implemented.


Patch:
diff --git a/Changelog.md b/Changelog.md
index 45d4100..4e05eef 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -23,6 +23,9 @@ MSRV bumped to 1.56! Crate now uses Rust 2021 edition.
 - [#660]: Fixed incorrect deserialization of `xs:list`s from empty tags (`<tag/>`
   or `<tag></tag>`). Previously an `DeError::UnexpectedEof")` was returned in that case
 - [#580]: Fixed incorrect deserialization of vectors of newtypes from sequences of tags.
+- [#661]: More string handling of serialized primitive values (booleans, numbers, strings,
+  unit structs, unit variants). `<int>123<something-else/></int>` is no longer valid
+  content. Previously all data after `123` up to closing tag would be silently skipped.
 
 ### Misc Changes
 
@@ -43,6 +46,7 @@ MSRV bumped to 1.56! Crate now uses Rust 2021 edition.
 [#649]: https://github.com/tafia/quick-xml/pull/646
 [#651]: https://github.com/tafia/quick-xml/pull/651
 [#660]: https://github.com/tafia/quick-xml/pull/660
+[#661]: https://github.com/tafia/quick-xml/pull/661
 
 
 ## 0.30.0 -- 2023-07-23
diff --git a/src/de/mod.rs b/src/de/mod.rs
index eb60972..e3e3717 100644
--- a/src/de/mod.rs
+++ b/src/de/mod.rs
@@ -2581,11 +2581,15 @@ where
     /// events, merge them into one string. If there are no such events, returns
     /// an empty string.
     ///
-    /// If `allow_start` is `false`, then only text events is consumed, for other
+    /// If `allow_start` is `false`, then only text events are consumed, for other
     /// events an error is returned (see table below).
     ///
-    /// If `allow_start` is `true`, then first [`DeEvent::Text`] event is returned
-    /// and all other content is skipped until corresponding end tag will be consumed.
+    /// If `allow_start` is `true`, then two or three events are expected:
+    /// - [`DeEvent::Start`];
+    /// - _(optional)_ [`DeEvent::Text`] which content is returned;
+    /// - [`DeEvent::End`]. If text event was missed, an empty string is returned.
+    ///
+    /// Corresponding events are consumed.
     ///
     /// # Handling events
     ///
@@ -2603,9 +2607,8 @@ where
     /// |Event             |XML                        |Handling
     /// |------------------|---------------------------|----------------------------------------------------------------------------------
     /// |[`DeEvent::Start`]|`<any-tag>...</any-tag>`   |Emits [`UnexpectedStart("any-tag")`](DeError::UnexpectedStart)
-    /// |[`DeEvent::End`]  |`</tag>`                   |Returns an empty slice, if close tag matched the open one
-    /// |[`DeEvent::End`]  |`</any-tag>`               |Emits [`UnexpectedEnd("any-tag")`](DeError::UnexpectedEnd)
-    /// |[`DeEvent::Text`] |`text content` or `<![CDATA[cdata content]]>` (probably mixed)|Returns event content unchanged, consumes events up to `</tag>`
+    /// |[`DeEvent::End`]  |`</tag>`                   |Returns an empty slice. The reader guarantee that tag will match the open one
+    /// |[`DeEvent::Text`] |`text content` or `<![CDATA[cdata content]]>` (probably mixed)|Returns event content unchanged, expects the `</tag>` after that
     /// |[`DeEvent::Eof`]  |                           |Emits [`UnexpectedEof`](DeError::UnexpectedEof)
     ///
     /// [`Text`]: Event::Text
@@ -2614,23 +2617,32 @@ where
         match self.next()? {
             DeEvent::Text(e) => Ok(e.text),
             // allow one nested level
-            DeEvent::Start(e) if allow_start => match self.next()? {
-                DeEvent::Text(t) => {
-                    self.read_to_end(e.name())?;
-                    Ok(t.text)
-                }
-                DeEvent::Start(s) => Err(DeError::UnexpectedStart(s.name().as_ref().to_owned())),
-                // We can get End event in case of `<tag></tag>` or `<tag/>` input
-                // Return empty text in that case
-                DeEvent::End(end) if end.name() == e.name() => Ok("".into()),
-                DeEvent::End(end) => Err(DeError::UnexpectedEnd(end.name().as_ref().to_owned())),
-                DeEvent::Eof => Err(DeError::UnexpectedEof),
-            },
+            DeEvent::Start(_) if allow_start => self.read_text(),
             DeEvent::Start(e) => Err(DeError::UnexpectedStart(e.name().as_ref().to_owned())),
             DeEvent::End(e) => Err(DeError::UnexpectedEnd(e.name().as_ref().to_owned())),
             DeEvent::Eof => Err(DeError::UnexpectedEof),
         }
     }
+    /// Consumes one [`DeEvent::Text`] event and ensures that it is followed by the
+    /// [`DeEvent::End`] event.
+    fn read_text(&mut self) -> Result<Cow<'de, str>, DeError> {
+        match self.next()? {
+            DeEvent::Text(e) => match self.next()? {
+                // The matching tag name is guaranteed by the reader
+                DeEvent::End(_) => Ok(e.text),
+                // SAFETY: Cannot be two consequent Text events, they would be merged into one
+                DeEvent::Text(_) => unreachable!(),
+                DeEvent::Start(e) => Err(DeError::UnexpectedStart(e.name().as_ref().to_owned())),
+                DeEvent::Eof => Err(DeError::UnexpectedEof),
+            },
+            // We can get End event in case of `<tag></tag>` or `<tag/>` input
+            // Return empty text in that case
+            // The matching tag name is guaranteed by the reader
+            DeEvent::End(_) => Ok("".into()),
+            DeEvent::Start(s) => Err(DeError::UnexpectedStart(s.name().as_ref().to_owned())),
+            DeEvent::Eof => Err(DeError::UnexpectedEof),
+        }
+    }
 
     /// Drops all events until event with [name](BytesEnd::name()) `name` won't be
     /// dropped. This method should be called after [`Self::next()`]


Test patch:
diff --git a/tests/serde-de.rs b/tests/serde-de.rs
index f31ed5e..b20d906 100644
--- a/tests/serde-de.rs
+++ b/tests/serde-de.rs
@@ -183,32 +183,97 @@ mod trivial {
         eof!("<!--comment-->");
     }
 
-    /// Tests deserialization from top-level tag content: `<root>...content...</root>`
-    mod struct_ {
-        use super::*;
+    macro_rules! in_struct {
+        ($name:ident: $type:ty = $value:expr, $expected:expr) => {
+            mod $name {
+                use super::*;
+                use pretty_assertions::assert_eq;
 
-        /// Well-formed XML must have a single tag at the root level.
-        /// Any XML tag can be modeled as a struct, and content of this tag are modeled as
-        /// fields of this struct.
-        ///
-        /// Because we want to get access to unnamed content of the tag (usually, this internal
-        /// XML node called `$text`) we use a rename to a special name `$text`
-        #[derive(Debug, Deserialize, PartialEq)]
-        struct Trivial<T> {
-            #[serde(rename = "$text")]
-            value: T,
-        }
+                #[test]
+                fn naked() {
+                    let item: $type = from_str(&format!("<root>{}</root>", $value)).unwrap();
+                    let expected: $type = $expected;
+                    assert_eq!(item, expected);
+
+                    match from_str::<$type>(&format!("<root><nested>{}</nested></root>", $value)) {
+                        // Expected unexpected start element `<nested>`
+                        Err(DeError::UnexpectedStart(tag)) => assert_eq!(tag, b"nested"),
+                        x => panic!(
+                            r#"Expected `Err(DeError::UnexpectedStart("nested"))`, but got `{:?}`"#,
+                            x
+                        ),
+                    }
+
+                    match from_str::<$type>(&format!("<root>{}<something-else/></root>", $value)) {
+                        // Expected unexpected start element `<something-else>`
+                        Err(DeError::UnexpectedStart(tag)) => assert_eq!(tag, b"something-else"),
+                        x => panic!(
+                            r#"Expected `Err(DeError::UnexpectedStart("something-else"))`, but got `{:?}`"#,
+                            x
+                        ),
+                    }
+
+                    match from_str::<$type>(&format!("<root><something-else/>{}</root>", $value)) {
+                        // Expected unexpected start element `<something-else>`
+                        Err(DeError::UnexpectedStart(tag)) => assert_eq!(tag, b"something-else"),
+                        x => panic!(
+                            r#"Expected `Err(DeError::UnexpectedStart("something-else"))`, but got `{:?}`"#,
+                            x
+                        ),
+                    }
+                }
 
-        macro_rules! in_struct {
-            ($name:ident: $type:ty = $value:expr, $expected:expr) => {
                 #[test]
-                fn $name() {
-                    let item: Trivial<$type> = from_str($value).unwrap();
+                fn field() {
+                    let item: Field<$type> = from_str(&format!("<root><value>{}</value></root>", $value)).unwrap();
+                    assert_eq!(item, Field { value: $expected });
 
+                    match from_str::<Field<$type>>(&format!("<root><value><nested>{}</nested></value></root>", $value)) {
+                        // Expected unexpected start element `<nested>`
+                        Err(DeError::UnexpectedStart(tag)) => assert_eq!(tag, b"nested"),
+                        x => panic!(
+                            r#"Expected `Err(DeError::UnexpectedStart("nested"))`, but got `{:?}`"#,
+                            x
+                        ),
+                    }
+
+                    match from_str::<Field<$type>>(&format!("<root><value>{}<something-else/></value></root>", $value)) {
+                        // Expected unexpected start element `<something-else>`
+                        Err(DeError::UnexpectedStart(tag)) => assert_eq!(tag, b"something-else"),
+                        x => panic!(
+                            r#"Expected `Err(DeError::UnexpectedStart("something-else"))`, but got `{:?}`"#,
+                            x
+                        ),
+                    }
+
+                    match from_str::<Field<$type>>(&format!("<root><value><something-else/>{}</value></root>", $value)) {
+                        // Expected unexpected start element `<something-else>`
+                        Err(DeError::UnexpectedStart(tag)) => assert_eq!(tag, b"something-else"),
+                        x => panic!(
+                            r#"Expected `Err(DeError::UnexpectedStart("something-else"))`, but got `{:?}`"#,
+                            x
+                        ),
+                    }
+                }
+
+                /// Tests deserialization from top-level tag content: `<root>...content...</root>`
+                #[test]
+                fn text() {
+                    let item: Trivial<$type> = from_str(&format!("<root>{}</root>", $value)).unwrap();
+                    assert_eq!(item, Trivial { value: $expected });
+
+                    // Unlike `naked` test, here we have a struct that is serialized to XML with
+                    // an implicit field `$text` and some other field "something-else" which not interested
+                    // for us in the Trivial structure. If you want the same behavior as for naked primitive,
+                    // use `$value` field which would consume all data, unless a dedicated field would present
+                    let item: Trivial<$type> = from_str(&format!("<root>{}<something-else/></root>", $value)).unwrap();
                     assert_eq!(item, Trivial { value: $expected });
 
-                    match from_str::<Trivial<$type>>(&format!("<outer>{}</outer>", $value)) {
-                        // Expected unexpected start element `<root>`
+                    let item: Trivial<$type> = from_str(&format!("<root><something-else/>{}</root>", $value)).unwrap();
+                    assert_eq!(item, Trivial { value: $expected });
+
+                    match from_str::<Trivial<$type>>(&format!("<root><nested>{}</nested></root>", $value)) {
+                        // Expected unexpected start element `<nested>`
                         Err(DeError::Custom(reason)) => assert_eq!(reason, "missing field `$text`"),
                         x => panic!(
                             r#"Expected `Err(DeError::Custom("missing field `$text`"))`, but got `{:?}`"#,
@@ -216,130 +281,147 @@ mod trivial {
                         ),
                     }
                 }
-            };
-        }
+            }
+        };
+    }
 
-        /// Tests deserialization from text content in a tag
-        #[rustfmt::skip] // tests formatted in a table
-        mod text {
-            use super::*;
-            use pretty_assertions::assert_eq;
+    #[derive(Debug, Deserialize, PartialEq)]
+    struct Field<T> {
+        value: T,
+    }
 
-            in_struct!(i8_:    i8    = "<root>-42</root>", -42i8);
-            in_struct!(i16_:   i16   = "<root>-4200</root>", -4200i16);
-            in_struct!(i32_:   i32   = "<root>-42000000</root>", -42000000i32);
-            in_struct!(i64_:   i64   = "<root>-42000000000000</root>", -42000000000000i64);
-            in_struct!(isize_: isize = "<root>-42000000000000</root>", -42000000000000isize);
+    /// Well-formed XML must have a single tag at the root level.
+    /// Any XML tag can be modeled as a struct, and content of this tag are modeled as
+    /// fields of this struct.
+    ///
+    /// Because we want to get access to unnamed content of the tag (usually, this internal
+    /// XML node called `$text`) we use a rename to a special name `$text`
+    #[derive(Debug, Deserialize, PartialEq)]
+    struct Trivial<T> {
+        #[serde(rename = "$text")]
+        value: T,
+    }
 
-            in_struct!(u8_:    u8    = "<root>42</root>", 42u8);
-            in_struct!(u16_:   u16   = "<root>4200</root>", 4200u16);
-            in_struct!(u32_:   u32   = "<root>42000000</root>", 42000000u32);
-            in_struct!(u64_:   u64   = "<root>42000000000000</root>", 42000000000000u64);
-            in_struct!(usize_: usize = "<root>42000000000000</root>", 42000000000000usize);
+    /// Tests deserialization from text content in a tag
+    #[rustfmt::skip] // tests formatted in a table
+    mod text {
+        use super::*;
+        use pretty_assertions::assert_eq;
 
-            serde_if_integer128! {
-                in_struct!(u128_: u128 = "<root>420000000000000000000000000000</root>", 420000000000000000000000000000u128);
-                in_struct!(i128_: i128 = "<root>-420000000000000000000000000000</root>", -420000000000000000000000000000i128);
-            }
+        in_struct!(i8_:    i8    = "-42", -42i8);
+        in_struct!(i16_:   i16   = "-4200", -4200i16);
+        in_struct!(i32_:   i32   = "-42000000", -42000000i32);
+        in_struct!(i64_:   i64   = "-42000000000000", -42000000000000i64);
+        in_struct!(isize_: isize = "-42000000000000", -42000000000000isize);
 
-            in_struct!(f32_: f32 = "<root>4.2</root>", 4.2f32);
-            in_struct!(f64_: f64 = "<root>4.2</root>", 4.2f64);
+        in_struct!(u8_:    u8    = "42", 42u8);
+        in_struct!(u16_:   u16   = "4200", 4200u16);
+        in_struct!(u32_:   u32   = "42000000", 42000000u32);
+        in_struct!(u64_:   u64   = "42000000000000", 42000000000000u64);
+        in_struct!(usize_: usize = "42000000000000", 42000000000000usize);
 
-            in_struct!(false_: bool = "<root>false</root>", false);
-            in_struct!(true_: bool = "<root>true</root>", true);
-            in_struct!(char_: char = "<root>r</root>", 'r');
+        serde_if_integer128! {
+            in_struct!(u128_: u128 = "420000000000000000000000000000", 420000000000000000000000000000u128);
+            in_struct!(i128_: i128 = "-420000000000000000000000000000", -420000000000000000000000000000i128);
+        }
 
-            in_struct!(string: String = "<root>escaped&#x20;string</root>", "escaped string".into());
+        in_struct!(f32_: f32 = "4.2", 4.2f32);
+        in_struct!(f64_: f64 = "4.2", 4.2f64);
 
-            /// XML does not able to store binary data
-            #[test]
-            fn byte_buf() {
-                match from_str::<Trivial<ByteBuf>>("<root>escaped&#x20;byte_buf</root>") {
-                    Err(DeError::Unsupported(msg)) => {
-                        assert_eq!(msg, "binary data content is not supported by XML format")
-                    }
-                    x => panic!(
-                        r#"Expected `Err(DeError::Unsupported("binary data content is not supported by XML format"))`, but got `{:?}`"#,
-                        x
-                    ),
+        in_struct!(false_: bool = "false", false);
+        in_struct!(true_: bool = "true", true);
+        in_struct!(char_: char = "r", 'r');
+
+        in_struct!(string: String = "escaped&#x20;string", "escaped string".into());
+
+        /// XML does not able to store binary data
+        #[test]
+        fn byte_buf() {
+            match from_str::<Trivial<ByteBuf>>("<root>escaped&#x20;byte_buf</root>") {
+                Err(DeError::Unsupported(msg)) => {
+                    assert_eq!(msg, "binary data content is not supported by XML format")
                 }
+                x => panic!(
+                    r#"Expected `Err(DeError::Unsupported("binary data content is not supported by XML format"))`, but got `{:?}`"#,
+                    x
+                ),
             }
+        }
 
-            /// XML does not able to store binary data
-            #[test]
-            fn bytes() {
-                match from_str::<Trivial<Bytes>>("<root>escaped&#x20;byte_buf</root>") {
-                    Err(DeError::Unsupported(msg)) => {
-                        assert_eq!(msg, "binary data content is not supported by XML format")
-                    }
-                    x => panic!(
-                        r#"Expected `Err(DeError::Unsupported("binary data content is not supported by XML format"))`, but got `{:?}`"#,
-                        x
-                    ),
+        /// XML does not able to store binary data
+        #[test]
+        fn bytes() {
+            match from_str::<Trivial<Bytes>>("<root>escaped&#x20;byte_buf</root>") {
+                Err(DeError::Unsupported(msg)) => {
+                    assert_eq!(msg, "binary data content is not supported by XML format")
                 }
+                x => panic!(
+                    r#"Expected `Err(DeError::Unsupported("binary data content is not supported by XML format"))`, but got `{:?}`"#,
+                    x
+                ),
             }
         }
+    }
 
-        /// Tests deserialization from CDATA content in a tag.
-        /// CDATA handling similar to text handling except that strings does not unescapes
-        #[rustfmt::skip] // tests formatted in a table
-        mod cdata {
-            use super::*;
-            use pretty_assertions::assert_eq;
+    /// Tests deserialization from CDATA content in a tag.
+    /// CDATA handling similar to text handling except that strings does not unescapes
+    #[rustfmt::skip] // tests formatted in a table
+    mod cdata {
+        use super::*;
+        use pretty_assertions::assert_eq;
 
-            in_struct!(i8_:    i8    = "<root><![CDATA[-42]]></root>", -42i8);
-            in_struct!(i16_:   i16   = "<root><![CDATA[-4200]]></root>", -4200i16);
-            in_struct!(i32_:   i32   = "<root><![CDATA[-42000000]]></root>", -42000000i32);
-            in_struct!(i64_:   i64   = "<root><![CDATA[-42000000000000]]></root>", -42000000000000i64);
-            in_struct!(isize_: isize = "<root><![CDATA[-42000000000000]]></root>", -42000000000000isize);
+        in_struct!(i8_:    i8    = "<![CDATA[-42]]>", -42i8);
+        in_struct!(i16_:   i16   = "<![CDATA[-4200]]>", -4200i16);
+        in_struct!(i32_:   i32   = "<![CDATA[-42000000]]>", -42000000i32);
+        in_struct!(i64_:   i64   = "<![CDATA[-42000000000000]]>", -42000000000000i64);
+        in_struct!(isize_: isize = "<![CDATA[-42000000000000]]>", -42000000000000isize);
 
-            in_struct!(u8_:    u8    = "<root><![CDATA[42]]></root>", 42u8);
-            in_struct!(u16_:   u16   = "<root><![CDATA[4200]]></root>", 4200u16);
-            in_struct!(u32_:   u32   = "<root><![CDATA[42000000]]></root>", 42000000u32);
-            in_struct!(u64_:   u64   = "<root><![CDATA[42000000000000]]></root>", 42000000000000u64);
-            in_struct!(usize_: usize = "<root><![CDATA[42000000000000]]></root>", 42000000000000usize);
+        in_struct!(u8_:    u8    = "<![CDATA[42]]>", 42u8);
+        in_struct!(u16_:   u16   = "<![CDATA[4200]]>", 4200u16);
+        in_struct!(u32_:   u32   = "<![CDATA[42000000]]>", 42000000u32);
+        in_struct!(u64_:   u64   = "<![CDATA[42000000000000]]>", 42000000000000u64);
+        in_struct!(usize_: usize = "<![CDATA[42000000000000]]>", 42000000000000usize);
 
-            serde_if_integer128! {
-                in_struct!(u128_: u128 = "<root><![CDATA[420000000000000000000000000000]]></root>", 420000000000000000000000000000u128);
-                in_struct!(i128_: i128 = "<root><![CDATA[-420000000000000000000000000000]]></root>", -420000000000000000000000000000i128);
-            }
+        serde_if_integer128! {
+            in_struct!(u128_: u128 = "<![CDATA[420000000000000000000000000000]]>", 420000000000000000000000000000u128);
+            in_struct!(i128_: i128 = "<![CDATA[-420000000000000000000000000000]]>", -420000000000000000000000000000i128);
+        }
 
-            in_struct!(f32_: f32 = "<root><![CDATA[4.2]]></root>", 4.2f32);
-            in_struct!(f64_: f64 = "<root><![CDATA[4.2]]></root>", 4.2f64);
+        in_struct!(f32_: f32 = "<![CDATA[4.2]]>", 4.2f32);
+        in_struct!(f64_: f64 = "<![CDATA[4.2]]>", 4.2f64);
 
-            in_struct!(false_: bool = "<root><![CDATA[false]]></root>", false);
-            in_struct!(true_: bool = "<root><![CDATA[true]]></root>", true);
-            in_struct!(char_: char = "<root><![CDATA[r]]></root>", 'r');
+        in_struct!(false_: bool = "<![CDATA[false]]>", false);
+        in_struct!(true_: bool = "<![CDATA[true]]>", true);
+        in_struct!(char_: char = "<![CDATA[r]]>", 'r');
 
-            // Escape sequences does not processed inside CDATA section
-            in_struct!(string: String = "<root><![CDATA[escaped&#x20;string]]></root>", "escaped&#x20;string".into());
+        // Escape sequences does not processed inside CDATA section
+        in_struct!(string: String = "<![CDATA[escaped&#x20;string]]>", "escaped&#x20;string".into());
 
-            /// XML does not able to store binary data
-            #[test]
-            fn byte_buf() {
-                match from_str::<Trivial<ByteBuf>>("<root><![CDATA[escaped&#x20;byte_buf]]></root>") {
-                    Err(DeError::Unsupported(msg)) => {
-                        assert_eq!(msg, "binary data content is not supported by XML format")
-                    }
-                    x => panic!(
-                        r#"Expected `Err(DeError::Unsupported("binary data content is not supported by XML format"))`, but got `{:?}`"#,
-                        x
-                    ),
+        /// XML does not able to store binary data
+        #[test]
+        fn byte_buf() {
+            match from_str::<Trivial<ByteBuf>>("<root><![CDATA[escaped&#x20;byte_buf]]></root>") {
+                Err(DeError::Unsupported(msg)) => {
+                    assert_eq!(msg, "binary data content is not supported by XML format")
                 }
+                x => panic!(
+                    r#"Expected `Err(DeError::Unsupported("binary data content is not supported by XML format"))`, but got `{:?}`"#,
+                    x
+                ),
             }
+        }
 
-            /// XML does not able to store binary data
-            #[test]
-            fn bytes() {
-                match from_str::<Trivial<Bytes>>("<root><![CDATA[escaped&#x20;byte_buf]]></root>") {
-                    Err(DeError::Unsupported(msg)) => {
-                        assert_eq!(msg, "binary data content is not supported by XML format")
-                    }
-                    x => panic!(
-                        r#"Expected `Err(DeError::Unsupported("binary data content is not supported by XML format"))`, but got `{:?}`"#,
-                        x
-                    ),
+        /// XML does not able to store binary data
+        #[test]
+        fn bytes() {
+            match from_str::<Trivial<Bytes>>("<root><![CDATA[escaped&#x20;byte_buf]]></root>") {
+                Err(DeError::Unsupported(msg)) => {
+                    assert_eq!(msg, "binary data content is not supported by XML format")
                 }
+                x => panic!(
+                    r#"Expected `Err(DeError::Unsupported("binary data content is not supported by XML format"))`, but got `{:?}`"#,
+                    x
+                ),
             }
         }
     }