Skip to content

Commit 980d8ae

Browse files
committed
Allow to deserialize event names into borrowed fields
1 parent f5155c6 commit 980d8ae

File tree

6 files changed

+60
-7
lines changed

6 files changed

+60
-7
lines changed

Changelog.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
- [#568]: Rename `Writter::inner` into `Writter::get_mut`
2424
- [#568]: Add method `Writter::get_ref`
2525
- [#569]: Rewrite the `Reader::read_event_into_async` as an async fn, making the future `Send` if possible.
26+
- [#571]: Borrow element names (`<element>`) when deserialize with serde.
27+
This change allow to deserialize into `HashMap<&str, T>`, for example
2628

2729
### Bug Fixes
2830

@@ -55,6 +57,7 @@
5557
[#565]: https://github.com/tafia/quick-xml/pull/565
5658
[#568]: https://github.com/tafia/quick-xml/pull/568
5759
[#569]: https://github.com/tafia/quick-xml/pull/569
60+
[#571]: https://github.com/tafia/quick-xml/pull/571
5861

5962
## 0.27.1 -- 2022-12-28
6063

src/de/key.rs

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -90,10 +90,22 @@ impl<'i, 'd> QNameDeserializer<'i, 'd> {
9090
}
9191

9292
/// Creates deserializer from name of an element
93-
pub fn from_elem(name: QName<'d>, decoder: Decoder) -> Result<Self, DeError> {
94-
let local = match decode_name(name, decoder)? {
95-
Cow::Borrowed(borrowed) => CowRef::Slice(borrowed),
96-
Cow::Owned(owned) => CowRef::Owned(owned),
93+
pub fn from_elem(name: CowRef<'i, 'd, [u8]>, decoder: Decoder) -> Result<Self, DeError> {
94+
let local = match name {
95+
CowRef::Input(borrowed) => match decode_name(QName(borrowed), decoder)? {
96+
Cow::Borrowed(borrowed) => CowRef::Input(borrowed),
97+
Cow::Owned(owned) => CowRef::Owned(owned),
98+
},
99+
CowRef::Slice(borrowed) => match decode_name(QName(borrowed), decoder)? {
100+
Cow::Borrowed(borrowed) => CowRef::Slice(borrowed),
101+
Cow::Owned(owned) => CowRef::Owned(owned),
102+
},
103+
CowRef::Owned(owned) => match decode_name(QName(&owned), decoder)? {
104+
// SAFETY: Because result is borrowed, no changes was done
105+
// and we can safely unwrap here
106+
Cow::Borrowed(_) => CowRef::Owned(String::from_utf8(owned).unwrap()),
107+
Cow::Owned(owned) => CowRef::Owned(owned),
108+
},
97109
};
98110

99111
Ok(Self { name: local })

src/de/map.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,7 @@ where
281281
DeEvent::Start(e) => {
282282
self.source = ValueSource::Nested;
283283

284-
let de = QNameDeserializer::from_elem(e.name(), decoder)?;
284+
let de = QNameDeserializer::from_elem(e.raw_name(), decoder)?;
285285
seed.deserialize(de).map(Some)
286286
}
287287
// Stop iteration after reaching a closing tag

src/de/var.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ where
3838
let decoder = self.de.reader.decoder();
3939
let (name, is_text) = match self.de.peek()? {
4040
DeEvent::Start(e) => (
41-
seed.deserialize(QNameDeserializer::from_elem(e.name(), decoder)?)?,
41+
seed.deserialize(QNameDeserializer::from_elem(e.raw_name(), decoder)?)?,
4242
false,
4343
),
4444
DeEvent::Text(_) => (

src/events/mod.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ use crate::errors::{Error, Result};
4949
use crate::escape::{escape, partial_escape, unescape_with};
5050
use crate::name::{LocalName, QName};
5151
use crate::reader::is_whitespace;
52-
use crate::utils::write_cow_string;
52+
use crate::utils::{write_cow_string, CowRef};
5353
use attributes::{Attribute, Attributes};
5454
use std::mem::replace;
5555

@@ -189,6 +189,21 @@ impl<'a> BytesStart<'a> {
189189
self.name_len = name.len();
190190
self
191191
}
192+
193+
/// Gets the undecoded raw tag name, as present in the input stream, which
194+
/// is borrowed either to the input, or to the event.
195+
///
196+
/// # Lifetimes
197+
///
198+
/// - `'a`: Lifetime of the input data from which this event is borrow
199+
/// - `'e`: Lifetime of the concrete event instance
200+
// TODO: We should made this is a part of public API, but with safe wrapped for a name
201+
pub(crate) fn raw_name<'e>(&'e self) -> CowRef<'a, 'e, [u8]> {
202+
match self.buf {
203+
Cow::Borrowed(b) => CowRef::Input(&b[..self.name_len]),
204+
Cow::Owned(ref o) => CowRef::Slice(&o[..self.name_len]),
205+
}
206+
}
192207
}
193208

194209
/// Attribute-related methods

tests/serde-de.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6285,6 +6285,9 @@ fn from_str_should_ignore_encoding() {
62856285
/// Checks that deserializer is able to borrow data from the input
62866286
mod borrow {
62876287
use super::*;
6288+
use pretty_assertions::assert_eq;
6289+
use std::collections::BTreeMap;
6290+
use std::iter::FromIterator;
62886291

62896292
/// Struct that should borrow input to be able to deserialize successfully.
62906293
/// serde implicitly borrow `&str` and `&[u8]` even without `#[serde(borrow)]`
@@ -6403,4 +6406,24 @@ mod borrow {
64036406
}
64046407
}
64056408
}
6409+
6410+
#[test]
6411+
fn element_name() {
6412+
let data: BTreeMap<&str, &str> = from_str(
6413+
r#"
6414+
<root>
6415+
<element>element content</element>
6416+
text content
6417+
</root>"#,
6418+
)
6419+
.unwrap();
6420+
assert_eq!(
6421+
data,
6422+
BTreeMap::from_iter([
6423+
// Comment to prevent formatting in one line
6424+
("element", "element content"),
6425+
("$text", "text content"),
6426+
])
6427+
);
6428+
}
64066429
}

0 commit comments

Comments
 (0)