diff --git a/Changelog.md b/Changelog.md
index 013aade8..b029b633 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -22,6 +22,10 @@
### Bug Fixes
+- [#912]: Fix deserialization of numbers, booleans and characters that is space-wrapped, for example
+ ` 42 `. That space characters are usually indent added during serialization and
+ other XML serialization libraries trims them
+
### Misc Changes
- [#901]: Fix running tests on 32-bit architecture
@@ -30,6 +34,7 @@
[#353]: https://github.com/tafia/quick-xml/issues/353
[#901]: https://github.com/tafia/quick-xml/pull/901
[#909]: https://github.com/tafia/quick-xml/pull/909
+[#912]: https://github.com/tafia/quick-xml/pull/912
[`Serializer::text_format()`]: https://docs.rs/quick-xml/0.38.4/quick_xml/se/struct.Serializer.html#method.text_format
diff --git a/src/de/map.rs b/src/de/map.rs
index b3ddfa20..1b2879bf 100644
--- a/src/de/map.rs
+++ b/src/de/map.rs
@@ -11,7 +11,6 @@ use crate::{
events::attributes::IterState,
events::BytesStart,
name::QName,
- utils::CowRef,
};
use serde::de::value::BorrowedStrDeserializer;
use serde::de::{self, DeserializeSeed, Deserializer as _, MapAccess, SeqAccess, Visitor};
diff --git a/src/de/mod.rs b/src/de/mod.rs
index fab4915d..35ba64c6 100644
--- a/src/de/mod.rs
+++ b/src/de/mod.rs
@@ -1977,21 +1977,14 @@
// Also, macros should be imported before using them
use serde::serde_if_integer128;
-macro_rules! deserialize_num {
- ($deserialize:ident => $visit:ident, $($mut:tt)?) => {
+macro_rules! forward_to_simple_type {
+ ($deserialize:ident, $($mut:tt)?) => {
+ #[inline]
fn $deserialize($($mut)? self, visitor: V) -> Result
where
V: Visitor<'de>,
{
- // No need to unescape because valid integer representations cannot be escaped
- let text = self.read_string()?;
- match text.parse() {
- Ok(number) => visitor.$visit(number),
- Err(_) => match text {
- Cow::Borrowed(t) => visitor.visit_str(t),
- Cow::Owned(t) => visitor.visit_string(t),
- }
- }
+ SimpleTypeDeserializer::from_text(self.read_string()?).$deserialize(visitor)
}
};
}
@@ -2000,63 +1993,29 @@ macro_rules! deserialize_num {
/// byte arrays, booleans and identifiers.
macro_rules! deserialize_primitives {
($($mut:tt)?) => {
- deserialize_num!(deserialize_i8 => visit_i8, $($mut)?);
- deserialize_num!(deserialize_i16 => visit_i16, $($mut)?);
- deserialize_num!(deserialize_i32 => visit_i32, $($mut)?);
- deserialize_num!(deserialize_i64 => visit_i64, $($mut)?);
+ forward_to_simple_type!(deserialize_i8, $($mut)?);
+ forward_to_simple_type!(deserialize_i16, $($mut)?);
+ forward_to_simple_type!(deserialize_i32, $($mut)?);
+ forward_to_simple_type!(deserialize_i64, $($mut)?);
- deserialize_num!(deserialize_u8 => visit_u8, $($mut)?);
- deserialize_num!(deserialize_u16 => visit_u16, $($mut)?);
- deserialize_num!(deserialize_u32 => visit_u32, $($mut)?);
- deserialize_num!(deserialize_u64 => visit_u64, $($mut)?);
+ forward_to_simple_type!(deserialize_u8, $($mut)?);
+ forward_to_simple_type!(deserialize_u16, $($mut)?);
+ forward_to_simple_type!(deserialize_u32, $($mut)?);
+ forward_to_simple_type!(deserialize_u64, $($mut)?);
serde_if_integer128! {
- deserialize_num!(deserialize_i128 => visit_i128, $($mut)?);
- deserialize_num!(deserialize_u128 => visit_u128, $($mut)?);
+ forward_to_simple_type!(deserialize_i128, $($mut)?);
+ forward_to_simple_type!(deserialize_u128, $($mut)?);
}
- deserialize_num!(deserialize_f32 => visit_f32, $($mut)?);
- deserialize_num!(deserialize_f64 => visit_f64, $($mut)?);
+ forward_to_simple_type!(deserialize_f32, $($mut)?);
+ forward_to_simple_type!(deserialize_f64, $($mut)?);
- fn deserialize_bool($($mut)? self, visitor: V) -> Result
- where
- V: Visitor<'de>,
- {
- let text = match self.read_string()? {
- Cow::Borrowed(s) => CowRef::Input(s),
- Cow::Owned(s) => CowRef::Owned(s),
- };
- text.deserialize_bool(visitor)
- }
+ forward_to_simple_type!(deserialize_bool, $($mut)?);
+ forward_to_simple_type!(deserialize_char, $($mut)?);
- /// Character represented as [strings](#method.deserialize_str).
- #[inline]
- fn deserialize_char(self, visitor: V) -> Result
- where
- V: Visitor<'de>,
- {
- self.deserialize_str(visitor)
- }
-
- fn deserialize_str($($mut)? self, visitor: V) -> Result
- where
- V: Visitor<'de>,
- {
- let text = self.read_string()?;
- match text {
- Cow::Borrowed(string) => visitor.visit_borrowed_str(string),
- Cow::Owned(string) => visitor.visit_string(string),
- }
- }
-
- /// Representation of owned strings the same as [non-owned](#method.deserialize_str).
- #[inline]
- fn deserialize_string(self, visitor: V) -> Result
- where
- V: Visitor<'de>,
- {
- self.deserialize_str(visitor)
- }
+ forward_to_simple_type!(deserialize_str, $($mut)?);
+ forward_to_simple_type!(deserialize_string, $($mut)?);
/// Forwards deserialization to the [`deserialize_any`](#method.deserialize_any).
#[inline]
@@ -2163,7 +2122,6 @@ use crate::{
events::{BytesCData, BytesEnd, BytesRef, BytesStart, BytesText, Event},
name::QName,
reader::NsReader,
- utils::CowRef,
};
use serde::de::{
self, Deserialize, DeserializeOwned, DeserializeSeed, IntoDeserializer, SeqAccess, Visitor,
@@ -2921,13 +2879,17 @@ where
/// [`CData`]: Event::CData
fn read_string_impl(&mut self, allow_start: bool) -> Result, DeError> {
match self.next()? {
+ // Reached by doc tests only: this file, lines 979 and 996
DeEvent::Text(e) => Ok(e.text),
// allow one nested level
+ // Reached by trivial::{...}::{field, field_nested, field_tag_after, field_tag_before, nested, tag_after, tag_before, wrapped}
DeEvent::Start(e) if allow_start => self.read_text(e.name()),
+ // TODO: not reached by any tests
DeEvent::Start(e) => Err(DeError::UnexpectedStart(e.name().as_ref().to_owned())),
// SAFETY: The reader is guaranteed that we don't have unmatched tags
// If we here, then our deserializer has a bug
DeEvent::End(e) => unreachable!("{:?}", e),
+ // Reached by trivial::{empty_doc, only_comment}
DeEvent::Eof => Err(DeError::UnexpectedEof),
}
}
@@ -2941,17 +2903,23 @@ where
match self.next()? {
DeEvent::Text(e) => match self.next()? {
// The matching tag name is guaranteed by the reader
+ // Reached by trivial::{...}::{field, wrapped}
DeEvent::End(_) => Ok(e.text),
// SAFETY: Cannot be two consequent Text events, they would be merged into one
DeEvent::Text(_) => unreachable!(),
+ // Reached by trivial::{...}::{field_tag_after, tag_after}
DeEvent::Start(e) => Err(DeError::UnexpectedStart(e.name().as_ref().to_owned())),
+ // Reached by struct_::non_closed::elements_child
DeEvent::Eof => Err(Error::missed_end(name, self.reader.decoder()).into()),
},
// We can get End event in case of `` or `` input
// Return empty text in that case
// The matching tag name is guaranteed by the reader
+ // Reached by {...}::xs_list::empty
DeEvent::End(_) => Ok("".into()),
+ // Reached by trivial::{...}::{field_nested, field_tag_before, nested, tag_before}
DeEvent::Start(s) => Err(DeError::UnexpectedStart(s.name().as_ref().to_owned())),
+ // Reached by struct_::non_closed::elements_child
DeEvent::Eof => Err(Error::missed_end(name, self.reader.decoder()).into()),
}
}
diff --git a/src/de/simple_type.rs b/src/de/simple_type.rs
index 41db1273..db0ab04a 100644
--- a/src/de/simple_type.rs
+++ b/src/de/simple_type.rs
@@ -7,7 +7,7 @@ use crate::de::Text;
use crate::encoding::Decoder;
use crate::errors::serialize::DeError;
use crate::escape::unescape;
-use crate::utils::CowRef;
+use crate::utils::{trim_xml_spaces, CowRef};
use memchr::memchr;
use serde::de::value::UnitDeserializer;
use serde::de::{
@@ -25,9 +25,9 @@ macro_rules! deserialize_num {
V: Visitor<'de>,
{
let text: &str = self.content.as_ref();
- match text.parse() {
+ match trim_xml_spaces(text).parse() {
Ok(number) => visitor.$visit(number),
- Err(_) => self.content.deserialize_str(visitor),
+ Err(_) => self.deserialize_str(visitor),
}
}
};
@@ -146,7 +146,20 @@ impl<'de, 'a> Deserializer<'de> for AtomicDeserializer<'de, 'a> {
where
V: Visitor<'de>,
{
- self.content.deserialize_bool(visitor)
+ let text = self.content.as_ref();
+ let text = if self.escaped {
+ unescape(text)?
+ } else {
+ Cow::Borrowed(text)
+ };
+ match trim_xml_spaces(&text) {
+ "1" | "true" => visitor.visit_bool(true),
+ "0" | "false" => visitor.visit_bool(false),
+ _ => match text {
+ Cow::Borrowed(_) => self.content.deserialize_str(visitor),
+ Cow::Owned(s) => visitor.visit_string(s),
+ },
+ }
}
deserialize_num!(deserialize_i8 => visit_i8);
@@ -172,7 +185,24 @@ impl<'de, 'a> Deserializer<'de> for AtomicDeserializer<'de, 'a> {
where
V: Visitor<'de>,
{
- self.deserialize_str(visitor)
+ let text: &str = self.content.as_ref();
+ let text = if self.escaped {
+ unescape(text)?
+ } else {
+ Cow::Borrowed(text)
+ };
+ let trimmed = trim_xml_spaces(&text);
+ // If string is empty or contains only XML space characters (probably only one),
+ // deserialize as usual string and allow visitor to accept or reject it.
+ // Otherwise trim spaces and allow visitor to accept or reject the rest.
+ if trimmed.is_empty() {
+ match text {
+ Cow::Borrowed(_) => self.content.deserialize_str(visitor),
+ Cow::Owned(s) => visitor.visit_string(s),
+ }
+ } else {
+ visitor.visit_str(trimmed)
+ }
}
/// Supply to the visitor borrowed string, string slice, or owned string
@@ -611,43 +641,11 @@ impl<'de, 'a> Deserializer<'de> for SimpleTypeDeserializer<'de, 'a> {
deserialize_primitive!(deserialize_f32);
deserialize_primitive!(deserialize_f64);
+ deserialize_primitive!(deserialize_char);
deserialize_primitive!(deserialize_str);
-
- /// Forwards deserialization to the [`Self::deserialize_str`]
- #[inline]
- fn deserialize_char(self, visitor: V) -> Result
- where
- V: Visitor<'de>,
- {
- self.deserialize_str(visitor)
- }
-
- /// Forwards deserialization to the [`Self::deserialize_str`]
- #[inline]
- fn deserialize_string(self, visitor: V) -> Result
- where
- V: Visitor<'de>,
- {
- self.deserialize_str(visitor)
- }
-
- /// Forwards deserialization to the [`Self::deserialize_str`]
- #[inline]
- fn deserialize_bytes(self, visitor: V) -> Result
- where
- V: Visitor<'de>,
- {
- self.deserialize_str(visitor)
- }
-
- /// Forwards deserialization to the [`Self::deserialize_str`]
- #[inline]
- fn deserialize_byte_buf(self, visitor: V) -> Result
- where
- V: Visitor<'de>,
- {
- self.deserialize_bytes(visitor)
- }
+ deserialize_primitive!(deserialize_string);
+ deserialize_primitive!(deserialize_bytes);
+ deserialize_primitive!(deserialize_byte_buf);
fn deserialize_option(self, visitor: V) -> Result
where
diff --git a/src/de/text.rs b/src/de/text.rs
index a72f3147..e8a0ad51 100644
--- a/src/de/text.rs
+++ b/src/de/text.rs
@@ -2,7 +2,6 @@ use crate::{
de::simple_type::SimpleTypeDeserializer,
de::{Text, TEXT_KEY},
errors::serialize::DeError,
- utils::CowRef,
};
use serde::de::value::BorrowedStrDeserializer;
use serde::de::{DeserializeSeed, Deserializer, EnumAccess, VariantAccess, Visitor};
diff --git a/src/utils.rs b/src/utils.rs
index bb4dc90e..6e91823d 100644
--- a/src/utils.rs
+++ b/src/utils.rs
@@ -375,6 +375,19 @@ pub const fn trim_xml_end(mut bytes: &[u8]) -> &[u8] {
bytes
}
+/// Returns a string slice with XML whitespace characters removed from both sides.
+///
+/// 'Whitespace' refers to the definition used by [`is_whitespace`].
+#[inline]
+pub fn trim_xml_spaces(text: &str) -> &str {
+ let bytes = trim_xml_end(trim_xml_start(text.as_bytes()));
+ match core::str::from_utf8(bytes) {
+ Ok(s) => s,
+ // SAFETY: Removing XML space characters (subset of ASCII) from a `&str` does not invalidate UTF-8.
+ _ => unreachable!(),
+ }
+}
+
////////////////////////////////////////////////////////////////////////////////////////////////////
/// Splits string into pieces which can be part of a single `CDATA` section.
diff --git a/tests/serde-de.rs b/tests/serde-de.rs
index b882b415..ff51ab11 100644
--- a/tests/serde-de.rs
+++ b/tests/serde-de.rs
@@ -178,17 +178,160 @@ mod trivial {
}
macro_rules! in_struct {
+ // string, char and bool have some specifics in some tests
+ (string: $type:ty = $value:expr, $expected:literal) => {
+ in_struct!(
+ string: $type = $value, $expected.into(), concat!(" \n\t", $expected, " \n\t").into();
+
+ /// Try to deserialize type from a root tag without data or with only spaces
+ #[test]
+ fn wrapped_empty() {
+ let item: $type = from_str("").unwrap();
+ let expected: $type = "".into();
+ assert_eq!(item, expected);
+
+ let item: $type = from_str(" \r\n\t").unwrap();
+ // \r\n normalized to \n
+ let expected: $type = " \n\t".into();
+ assert_eq!(item, expected);
+ }
+
+ #[test]
+ fn field_empty() {
+ let item: Field<$type> = from_str("").unwrap();
+ assert_eq!(item, Field { value: "".into() });
+
+ let item: Field<$type> = from_str(" \r\n\t").unwrap();
+ // \r\n normalized to \n
+ assert_eq!(item, Field { value: " \n\t".into() });
+ }
+ );
+ };
+ (char_: $type:ty = $value:expr, $expected:expr) => {
+ in_struct!(
+ char_: $type = $value, $expected, $expected;
+
+ /// Try to deserialize type from a root tag without data or with only spaces
+ #[test]
+ fn wrapped_empty() {
+ match from_str::<$type>("") {
+ Err(DeError::Custom(msg)) => assert_eq!(msg, r#"invalid value: string "", expected a character"#),
+ x => panic!("Expected `Err(Custom(_))`, but got `{:?}`", x),
+ }
+
+ match from_str::<$type>(" \r\n\t") {
+ // \r\n normalized to \n
+ Err(DeError::Custom(msg)) => assert_eq!(msg, r#"invalid value: string " \n\t", expected a character"#),
+ x => panic!("Expected `Err(Custom(_))`, but got `{:?}`", x),
+ }
+ }
+
+ #[test]
+ fn field_empty() {
+ match from_str::>("") {
+ Err(DeError::Custom(msg)) => assert_eq!(msg, r#"invalid value: string "", expected a character"#),
+ x => panic!("Expected `Err(Custom(_))`, but got `{:?}`", x),
+ }
+
+ match from_str::>(" \r\n\t") {
+ // \r\n normalized to \n
+ Err(DeError::Custom(msg)) => assert_eq!(msg, r#"invalid value: string " \n\t", expected a character"#),
+ x => panic!("Expected `Err(Custom(_))`, but got `{:?}`", x),
+ }
+ }
+ );
+ };
+ ($name:ident: bool = $value:expr, $expected:expr) => {
+ in_struct!(
+ $name: bool = $value, $expected, $expected;
+
+ /// Try to deserialize type from a root tag without data or with only spaces
+ #[test]
+ fn wrapped_empty() {
+ match from_str::("") {
+ Err(DeError::Custom(msg)) => assert_eq!(msg, r#"invalid type: string "", expected a boolean"#),
+ x => panic!("Expected `Err(Custom(_))`, but got `{:?}`", x),
+ }
+
+ match from_str::(" \r\n\t") {
+ // \r\n normalized to \n
+ Err(DeError::Custom(msg)) => assert_eq!(msg, r#"invalid type: string " \n\t", expected a boolean"#),
+ x => panic!("Expected `Err(Custom(_))`, but got `{:?}`", x),
+ }
+ }
+
+ #[test]
+ fn field_empty() {
+ match from_str::>("") {
+ Err(DeError::Custom(msg)) => assert_eq!(msg, r#"invalid type: string "", expected a boolean"#),
+ x => panic!("Expected `Err(Custom(_))`, but got `{:?}`", x),
+ }
+
+ match from_str::>(" \r\n\t") {
+ // \r\n normalized to \n
+ Err(DeError::Custom(msg)) => assert_eq!(msg, r#"invalid type: string " \n\t", expected a boolean"#),
+ x => panic!("Expected `Err(Custom(_))`, but got `{:?}`", x),
+ }
+ }
+ );
+ };
($name:ident: $type:ty = $value:expr, $expected:expr) => {
+ in_struct!(
+ $name: $type = $value, $expected, $expected;
+
+ /// Try to deserialize type from a root tag without data or with only spaces
+ #[test]
+ fn wrapped_empty() {
+ match from_str::<$type>("") {
+ Err(DeError::Custom(msg)) => assert_eq!(msg, concat!(r#"invalid type: string "", expected "#, stringify!($type))),
+ x => panic!("Expected `Err(Custom(_))`, but got `{:?}`", x),
+ }
+
+ match from_str::<$type>(" \r\n\t") {
+ // \r\n normalized to \n
+ Err(DeError::Custom(msg)) => assert_eq!(msg, concat!(r#"invalid type: string " \n\t", expected "#, stringify!($type))),
+ x => panic!("Expected `Err(Custom(_))`, but got `{:?}`", x),
+ }
+ }
+
+ #[test]
+ fn field_empty() {
+ match from_str::>("") {
+ Err(DeError::Custom(msg)) => assert_eq!(msg, concat!(r#"invalid type: string "", expected "#, stringify!($type))),
+ x => panic!("Expected `Err(Custom(_))`, but got `{:?}`", x),
+ }
+
+ match from_str::>(" \r\n\t") {
+ // \r\n normalized to \n
+ Err(DeError::Custom(msg)) => assert_eq!(msg, concat!(r#"invalid type: string " \n\t", expected "#, stringify!($type))),
+ x => panic!("Expected `Err(Custom(_))`, but got `{:?}`", x),
+ }
+ }
+ );
+ };
+ ($name:ident: $type:ty = $value:expr, $expected:expr, $expected_with_indent:expr; $($specific_tests:item)*) => {
mod $name {
use super::*;
use pretty_assertions::assert_eq;
+ $($specific_tests)*
+
+ /// Try to deserialize type wrapped in a tag, optionally surround with spaces
#[test]
- fn naked() {
+ fn wrapped() {
let item: $type = from_str(&format!("{}", $value)).unwrap();
let expected: $type = $expected;
assert_eq!(item, expected);
+ let item: $type =
+ from_str(&format!(" \r\n\t{} \r\n\t", $value)).unwrap();
+ let expected: $type = $expected_with_indent;
+ assert_eq!(item, expected);
+ }
+
+ /// Try to deserialize type wrapped in two tags
+ #[test]
+ fn nested() {
match from_str::<$type>(&format!("{}", $value)) {
// Expected unexpected start element ``
Err(DeError::UnexpectedStart(tag)) => assert_eq!(tag, b"nested"),
@@ -198,6 +341,22 @@ mod trivial {
),
}
+ match from_str::<$type>(&format!(
+ " \r\n\t{} \r\n\t",
+ $value
+ )) {
+ // Expected unexpected start element ``
+ Err(DeError::UnexpectedStart(tag)) => assert_eq!(tag, b"nested"),
+ x => panic!(
+ r#"Expected `Err(UnexpectedStart("nested"))`, but got `{:?}`"#,
+ x
+ ),
+ }
+ }
+
+ /// Try to deserialize type wrapped in a tag with another tag after content
+ #[test]
+ fn tag_after() {
match from_str::<$type>(&format!("{}", $value)) {
// Expected unexpected start element ``
Err(DeError::UnexpectedStart(tag)) => assert_eq!(tag, b"something-else"),
@@ -207,6 +366,22 @@ mod trivial {
),
}
+ match from_str::<$type>(&format!(
+ " \r\n\t{} \r\n\t",
+ $value
+ )) {
+ // Expected unexpected start element ``
+ Err(DeError::UnexpectedStart(tag)) => assert_eq!(tag, b"something-else"),
+ x => panic!(
+ r#"Expected `Err(UnexpectedStart("something-else"))`, but got `{:?}`"#,
+ x
+ ),
+ }
+ }
+
+ /// Try to deserialize type wrapped in a tag with another tag before content
+ #[test]
+ fn tag_before() {
match from_str::<$type>(&format!("{}", $value)) {
// Expected unexpected start element ``
Err(DeError::UnexpectedStart(tag)) => assert_eq!(tag, b"something-else"),
@@ -215,14 +390,34 @@ mod trivial {
x
),
}
+
+ match from_str::<$type>(&format!(
+ " \r\n\t{} \r\n\t",
+ $value
+ )) {
+ // Expected unexpected start element ``
+ Err(DeError::UnexpectedStart(tag)) => assert_eq!(tag, b"something-else"),
+ x => panic!(
+ r#"Expected `Err(UnexpectedStart("something-else"))`, but got `{:?}`"#,
+ x
+ ),
+ }
}
+ /// Try to deserialize type which is a field of a struct
#[test]
fn field() {
let item: Field<$type> =
from_str(&format!("{}", $value)).unwrap();
assert_eq!(item, Field { value: $expected });
+ let item: Field<$type> =
+ from_str(&format!(" \r\n\t{} \r\n\t", $value)).unwrap();
+ assert_eq!(item, Field { value: $expected_with_indent });
+ }
+
+ #[test]
+ fn field_nested() {
match from_str::>(&format!(
"{}",
$value
@@ -235,6 +430,21 @@ mod trivial {
),
}
+ match from_str::>(&format!(
+ " \r\n\t{} \r\n\t",
+ $value
+ )) {
+ // Expected unexpected start element ``
+ Err(DeError::UnexpectedStart(tag)) => assert_eq!(tag, b"nested"),
+ x => panic!(
+ r#"Expected `Err(UnexpectedStart("nested"))`, but got `{:?}`"#,
+ x
+ ),
+ }
+ }
+
+ #[test]
+ fn field_tag_after() {
match from_str::>(&format!(
"{}",
$value
@@ -247,6 +457,21 @@ mod trivial {
),
}
+ match from_str::>(&format!(
+ " \r\n\t{} \r\n\t",
+ $value
+ )) {
+ // Expected unexpected start element ``
+ Err(DeError::UnexpectedStart(tag)) => assert_eq!(tag, b"something-else"),
+ x => panic!(
+ r#"Expected `Err(UnexpectedStart("something-else"))`, but got `{:?}`"#,
+ x
+ ),
+ }
+ }
+
+ #[test]
+ fn field_tag_before() {
match from_str::>(&format!(
"{}",
$value
@@ -258,6 +483,31 @@ mod trivial {
x
),
}
+
+ match from_str::>(&format!(
+ " \r\n\t{} \r\n\t",
+ $value
+ )) {
+ // Expected unexpected start element ``
+ Err(DeError::UnexpectedStart(tag)) => assert_eq!(tag, b"something-else"),
+ x => panic!(
+ r#"Expected `Err(UnexpectedStart("something-else"))`, but got `{:?}`"#,
+ x
+ ),
+ }
+ }
+
+ #[test]
+ fn text_empty() {
+ match from_str::>("") {
+ Err(DeError::Custom(msg)) => assert_eq!(msg, "missing field `$text`"),
+ x => panic!("Expected `Err(Custom(_))`, but got `{:?}`", x),
+ }
+
+ match from_str::>(" \r\n\t") {
+ Err(DeError::Custom(msg)) => assert_eq!(msg, "missing field `$text`"),
+ x => panic!("Expected `Err(Custom(_))`, but got `{:?}`", x),
+ }
}
/// Tests deserialization from top-level tag content: `...content...`
@@ -267,18 +517,13 @@ mod trivial {
from_str(&format!("{}", $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!("{}", $value)).unwrap();
- assert_eq!(item, Trivial { value: $expected });
-
let item: Trivial<$type> =
- from_str(&format!("{}", $value)).unwrap();
- assert_eq!(item, Trivial { value: $expected });
+ from_str(&format!(" \r\n\t{} \r\n\t", $value)).unwrap();
+ assert_eq!(item, Trivial { value: $expected_with_indent });
+ }
+ #[test]
+ fn text_nested() {
match from_str::>(&format!(
"{}",
$value
@@ -290,6 +535,44 @@ mod trivial {
x
),
}
+
+ match from_str::>(&format!(
+ " \r\n\t{} \r\n\t",
+ $value
+ )) {
+ // Expected unexpected start element ``
+ Err(DeError::Custom(reason)) => assert_eq!(reason, "missing field `$text`"),
+ x => panic!(
+ r#"Expected `Err(Custom("missing field `$text`"))`, but got `{:?}`"#,
+ x
+ ),
+ }
+ }
+
+ #[test]
+ fn text_tag_after() {
+ // Unlike `wrapped` 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!("{}", $value)).unwrap();
+ assert_eq!(item, Trivial { value: $expected });
+
+ let item: Trivial<$type> =
+ from_str(&format!(" \r\n\t{} \r\n\t", $value)).unwrap();
+ assert_eq!(item, Trivial { value: $expected_with_indent });
+ }
+
+ #[test]
+ fn text_tag_before() {
+ let item: Trivial<$type> =
+ from_str(&format!("{}", $value)).unwrap();
+ assert_eq!(item, Trivial { value: $expected });
+
+ let item: Trivial<$type> =
+ from_str(&format!(" \r\n\t{} \r\n\t", $value)).unwrap();
+ assert_eq!(item, Trivial { value: $expected_with_indent });
}
}
};
@@ -342,7 +625,7 @@ mod trivial {
in_struct!(true_: bool = "true", true);
in_struct!(char_: char = "r", 'r');
- in_struct!(string: String = "escaped string", "escaped string".into());
+ in_struct!(string: String = "escaped string", "escaped string");
/// XML does not able to store binary data
#[test]
@@ -405,7 +688,7 @@ mod trivial {
in_struct!(char_: char = "", 'r');
// Escape sequences does not processed inside CDATA section
- in_struct!(string: String = "", "escaped string".into());
+ in_struct!(string: String = "", "escaped string");
/// XML does not able to store binary data
#[test]