Skip to content

Commit a6c5fc5

Browse files
authored
Merge pull request tafia#826 from Mingun/more-deserializers
Fix boolean parsing and make `SimpleTypeSerializer` and `SimpleTypeDeserializer` public
2 parents 3197e64 + 83d2957 commit a6c5fc5

File tree

9 files changed

+275
-241
lines changed

9 files changed

+275
-241
lines changed

Changelog.md

+16
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,21 @@
1515

1616
### New Features
1717

18+
- [#826]: Implement `From<String>` and `From<Cow<str>>` for `quick_xml::de::Text`.
19+
- [#826]: Make `SimpleTypeDeserializer` and `SimpleTypeSerializer` public.
20+
- [#826]: Implement `IntoDeserializer` for `&mut Deserializer`.
21+
1822
### Bug Fixes
1923

2024
- [#655]: Do not write indent before and after `$text` fields and those `$value` fields
2125
that are serialized as a text (for example, `usize` or `String`).
26+
- [#826]: Handle only those boolean representations that are allowed by [Xml Schema]
27+
which is only `"true"`, `"1"`, `"false"`, and `"0"`. Previously the following values
28+
also was accepted:
29+
|`bool` |XML content
30+
|-------|-------------------------------------------------------------
31+
|`true` |`"True"`, `"TRUE"`, `"t"`, `"Yes"`, `"YES"`, `"yes"`, `"y"`
32+
|`false`|`"False"`, `"FALSE"`, `"f"`, `"No"`, `"NO"`, `"no"`, `"n"`
2233

2334
### Misc Changes
2435

@@ -34,14 +45,19 @@
3445
`Vec<String>` in `$value` fields. They cannot be deserialized back with the same result
3546
- [#827]: Make `escape` and it variants take a `impl Into<Cow<str>>` argument and implement
3647
`From<(&'a str, Cow<'a, str>)>` on `Attribute`
48+
- [#826]: Removed `DeError::InvalidInt`, `DeError::InvalidFloat` and `DeError::InvalidBoolean`.
49+
Now the responsibility for returning the error lies with the visitor of the type.
50+
See rationale in https://github.com/serde-rs/serde/pull/2811
3751

3852
[#227]: https://github.com/tafia/quick-xml/issues/227
3953
[#655]: https://github.com/tafia/quick-xml/issues/655
4054
[#810]: https://github.com/tafia/quick-xml/pull/810
4155
[#811]: https://github.com/tafia/quick-xml/pull/811
4256
[#820]: https://github.com/tafia/quick-xml/pull/820
4357
[#823]: https://github.com/tafia/quick-xml/pull/823
58+
[#826]: https://github.com/tafia/quick-xml/pull/826
4459
[#827]: https://github.com/tafia/quick-xml/pull/827
60+
[Xml Schema]: https://www.w3.org/TR/xmlschema11-2/#boolean
4561

4662

4763
## 0.36.2 -- 2024-09-20

src/de/key.rs

+6-9
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
use crate::de::simple_type::UnitOnly;
2-
use crate::de::str2bool;
32
use crate::encoding::Decoder;
43
use crate::errors::serialize::DeError;
54
use crate::name::QName;
@@ -14,7 +13,10 @@ macro_rules! deserialize_num {
1413
where
1514
V: Visitor<'de>,
1615
{
17-
visitor.$visit(self.name.parse()?)
16+
match self.name.parse() {
17+
Ok(number) => visitor.$visit(number),
18+
Err(_) => self.name.deserialize_str(visitor),
19+
}
1820
}
1921
};
2022
}
@@ -134,17 +136,12 @@ impl<'de, 'd> Deserializer<'de> for QNameDeserializer<'de, 'd> {
134136

135137
/// According to the <https://www.w3.org/TR/xmlschema11-2/#boolean>,
136138
/// valid boolean representations are only `"true"`, `"false"`, `"1"`,
137-
/// and `"0"`. But this method also handles following:
138-
///
139-
/// |`bool` |XML content
140-
/// |-------|-------------------------------------------------------------
141-
/// |`true` |`"True"`, `"TRUE"`, `"t"`, `"Yes"`, `"YES"`, `"yes"`, `"y"`
142-
/// |`false`|`"False"`, `"FALSE"`, `"f"`, `"No"`, `"NO"`, `"no"`, `"n"`
139+
/// and `"0"`.
143140
fn deserialize_bool<V>(self, visitor: V) -> Result<V::Value, Self::Error>
144141
where
145142
V: Visitor<'de>,
146143
{
147-
str2bool(self.name.as_ref(), visitor)
144+
self.name.deserialize_bool(visitor)
148145
}
149146

150147
deserialize_num!(deserialize_i8, visit_i8);

src/de/map.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,14 @@ use crate::{
55
de::resolver::EntityResolver,
66
de::simple_type::SimpleTypeDeserializer,
77
de::text::TextDeserializer,
8-
de::{str2bool, DeEvent, Deserializer, XmlRead, TEXT_KEY, VALUE_KEY},
8+
de::{DeEvent, Deserializer, XmlRead, TEXT_KEY, VALUE_KEY},
99
encoding::Decoder,
1010
errors::serialize::DeError,
1111
errors::Error,
1212
events::attributes::IterState,
1313
events::BytesStart,
1414
name::QName,
15+
utils::CowRef,
1516
};
1617
use serde::de::value::BorrowedStrDeserializer;
1718
use serde::de::{self, DeserializeSeed, Deserializer as _, MapAccess, SeqAccess, Visitor};

src/de/mod.rs

+61-63
Original file line numberDiff line numberDiff line change
@@ -1834,15 +1834,21 @@
18341834
// Also, macros should be imported before using them
18351835
use serde::serde_if_integer128;
18361836

1837-
macro_rules! deserialize_type {
1837+
macro_rules! deserialize_num {
18381838
($deserialize:ident => $visit:ident, $($mut:tt)?) => {
18391839
fn $deserialize<V>($($mut)? self, visitor: V) -> Result<V::Value, DeError>
18401840
where
18411841
V: Visitor<'de>,
18421842
{
18431843
// No need to unescape because valid integer representations cannot be escaped
18441844
let text = self.read_string()?;
1845-
visitor.$visit(text.parse()?)
1845+
match text.parse() {
1846+
Ok(number) => visitor.$visit(number),
1847+
Err(_) => match text {
1848+
Cow::Borrowed(t) => visitor.visit_str(t),
1849+
Cow::Owned(t) => visitor.visit_string(t),
1850+
}
1851+
}
18461852
}
18471853
};
18481854
}
@@ -1851,31 +1857,33 @@ macro_rules! deserialize_type {
18511857
/// byte arrays, booleans and identifiers.
18521858
macro_rules! deserialize_primitives {
18531859
($($mut:tt)?) => {
1854-
deserialize_type!(deserialize_i8 => visit_i8, $($mut)?);
1855-
deserialize_type!(deserialize_i16 => visit_i16, $($mut)?);
1856-
deserialize_type!(deserialize_i32 => visit_i32, $($mut)?);
1857-
deserialize_type!(deserialize_i64 => visit_i64, $($mut)?);
1860+
deserialize_num!(deserialize_i8 => visit_i8, $($mut)?);
1861+
deserialize_num!(deserialize_i16 => visit_i16, $($mut)?);
1862+
deserialize_num!(deserialize_i32 => visit_i32, $($mut)?);
1863+
deserialize_num!(deserialize_i64 => visit_i64, $($mut)?);
18581864

1859-
deserialize_type!(deserialize_u8 => visit_u8, $($mut)?);
1860-
deserialize_type!(deserialize_u16 => visit_u16, $($mut)?);
1861-
deserialize_type!(deserialize_u32 => visit_u32, $($mut)?);
1862-
deserialize_type!(deserialize_u64 => visit_u64, $($mut)?);
1865+
deserialize_num!(deserialize_u8 => visit_u8, $($mut)?);
1866+
deserialize_num!(deserialize_u16 => visit_u16, $($mut)?);
1867+
deserialize_num!(deserialize_u32 => visit_u32, $($mut)?);
1868+
deserialize_num!(deserialize_u64 => visit_u64, $($mut)?);
18631869

18641870
serde_if_integer128! {
1865-
deserialize_type!(deserialize_i128 => visit_i128, $($mut)?);
1866-
deserialize_type!(deserialize_u128 => visit_u128, $($mut)?);
1871+
deserialize_num!(deserialize_i128 => visit_i128, $($mut)?);
1872+
deserialize_num!(deserialize_u128 => visit_u128, $($mut)?);
18671873
}
18681874

1869-
deserialize_type!(deserialize_f32 => visit_f32, $($mut)?);
1870-
deserialize_type!(deserialize_f64 => visit_f64, $($mut)?);
1875+
deserialize_num!(deserialize_f32 => visit_f32, $($mut)?);
1876+
deserialize_num!(deserialize_f64 => visit_f64, $($mut)?);
18711877

18721878
fn deserialize_bool<V>($($mut)? self, visitor: V) -> Result<V::Value, DeError>
18731879
where
18741880
V: Visitor<'de>,
18751881
{
1876-
let text = self.read_string()?;
1877-
1878-
str2bool(&text, visitor)
1882+
let text = match self.read_string()? {
1883+
Cow::Borrowed(s) => CowRef::Input(s),
1884+
Cow::Owned(s) => CowRef::Owned(s),
1885+
};
1886+
text.deserialize_bool(visitor)
18791887
}
18801888

18811889
/// Character represented as [strings](#method.deserialize_str).
@@ -1998,8 +2006,9 @@ mod simple_type;
19982006
mod text;
19992007
mod var;
20002008

2009+
pub use self::resolver::{EntityResolver, PredefinedEntityResolver};
2010+
pub use self::simple_type::SimpleTypeDeserializer;
20012011
pub use crate::errors::serialize::DeError;
2002-
pub use resolver::{EntityResolver, PredefinedEntityResolver};
20032012

20042013
use crate::{
20052014
de::map::ElementMapAccess,
@@ -2008,8 +2017,11 @@ use crate::{
20082017
events::{BytesCData, BytesEnd, BytesStart, BytesText, Event},
20092018
name::QName,
20102019
reader::Reader,
2020+
utils::CowRef,
2021+
};
2022+
use serde::de::{
2023+
self, Deserialize, DeserializeOwned, DeserializeSeed, IntoDeserializer, SeqAccess, Visitor,
20112024
};
2012-
use serde::de::{self, Deserialize, DeserializeOwned, DeserializeSeed, SeqAccess, Visitor};
20132025
use std::borrow::Cow;
20142026
#[cfg(feature = "overlapped-lists")]
20152027
use std::collections::VecDeque;
@@ -2058,6 +2070,22 @@ impl<'a> From<&'a str> for Text<'a> {
20582070
}
20592071
}
20602072

2073+
impl<'a> From<String> for Text<'a> {
2074+
#[inline]
2075+
fn from(text: String) -> Self {
2076+
Self {
2077+
text: Cow::Owned(text),
2078+
}
2079+
}
2080+
}
2081+
2082+
impl<'a> From<Cow<'a, str>> for Text<'a> {
2083+
#[inline]
2084+
fn from(text: Cow<'a, str>) -> Self {
2085+
Self { text }
2086+
}
2087+
}
2088+
20612089
////////////////////////////////////////////////////////////////////////////////////////////////////
20622090

20632091
/// Simplified event which contains only these variants that used by deserializer
@@ -2287,7 +2315,7 @@ where
22872315
}
22882316

22892317
/// Deserialize from a reader. This method will do internal copies of data
2290-
/// readed from `reader`. If you want have a `&str` input and want to borrow
2318+
/// read from `reader`. If you want have a `&str` input and want to borrow
22912319
/// as much as possible, use [`from_str`].
22922320
pub fn from_reader<R, T>(reader: R) -> Result<T, DeError>
22932321
where
@@ -2298,49 +2326,6 @@ where
22982326
T::deserialize(&mut de)
22992327
}
23002328

2301-
// TODO: According to the https://www.w3.org/TR/xmlschema11-2/#boolean,
2302-
// valid boolean representations are only "true", "false", "1", and "0"
2303-
fn str2bool<'de, V>(value: &str, visitor: V) -> Result<V::Value, DeError>
2304-
where
2305-
V: de::Visitor<'de>,
2306-
{
2307-
match value {
2308-
"true" | "1" | "True" | "TRUE" | "t" | "Yes" | "YES" | "yes" | "y" => {
2309-
visitor.visit_bool(true)
2310-
}
2311-
"false" | "0" | "False" | "FALSE" | "f" | "No" | "NO" | "no" | "n" => {
2312-
visitor.visit_bool(false)
2313-
}
2314-
_ => Err(DeError::InvalidBoolean(value.into())),
2315-
}
2316-
}
2317-
2318-
fn deserialize_bool<'de, V>(value: &[u8], decoder: Decoder, visitor: V) -> Result<V::Value, DeError>
2319-
where
2320-
V: Visitor<'de>,
2321-
{
2322-
#[cfg(feature = "encoding")]
2323-
{
2324-
let value = decoder.decode(value)?;
2325-
// No need to unescape because valid boolean representations cannot be escaped
2326-
str2bool(value.as_ref(), visitor)
2327-
}
2328-
2329-
#[cfg(not(feature = "encoding"))]
2330-
{
2331-
// No need to unescape because valid boolean representations cannot be escaped
2332-
match value {
2333-
b"true" | b"1" | b"True" | b"TRUE" | b"t" | b"Yes" | b"YES" | b"yes" | b"y" => {
2334-
visitor.visit_bool(true)
2335-
}
2336-
b"false" | b"0" | b"False" | b"FALSE" | b"f" | b"No" | b"NO" | b"no" | b"n" => {
2337-
visitor.visit_bool(false)
2338-
}
2339-
e => Err(DeError::InvalidBoolean(decoder.decode(e)?.into())),
2340-
}
2341-
}
2342-
}
2343-
23442329
////////////////////////////////////////////////////////////////////////////////////////////////////
23452330

23462331
/// A structure that deserializes XML into Rust values.
@@ -3007,6 +2992,19 @@ where
30072992
}
30082993
}
30092994

2995+
impl<'de, 'a, R, E> IntoDeserializer<'de, DeError> for &'a mut Deserializer<'de, R, E>
2996+
where
2997+
R: XmlRead<'de>,
2998+
E: EntityResolver,
2999+
{
3000+
type Deserializer = Self;
3001+
3002+
#[inline]
3003+
fn into_deserializer(self) -> Self {
3004+
self
3005+
}
3006+
}
3007+
30103008
////////////////////////////////////////////////////////////////////////////////////////////////////
30113009

30123010
/// Helper struct that contains a state for an algorithm of converting events

0 commit comments

Comments
 (0)