Skip to content

Commit 4ef3fb3

Browse files
Rémi Labeyriedralley
Rémi Labeyrie
authored andcommitted
Add Writer::write_serializable
Allow serializing individual objects using serde with the raw Writer API closes tafia#610
1 parent 5a536d0 commit 4ef3fb3

File tree

3 files changed

+173
-11
lines changed

3 files changed

+173
-11
lines changed

Changelog.md

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,17 @@
1212

1313
### New Features
1414

15+
- [#609]: Added `Writer::write_serializable` for serializing arbitrary types using serde
16+
when using the `Writer` API.
17+
1518
### Bug Fixes
1619

1720
### Misc Changes
1821

1922

23+
[#609]: https://github.com/tafia/quick-xml/issues/609
24+
25+
2026
## 0.29.0 -- 2023-06-13
2127

2228
### New Features
@@ -45,7 +51,6 @@
4551
[#606]: https://github.com/tafia/quick-xml/pull/606
4652
[#608]: https://github.com/tafia/quick-xml/issues/608
4753

48-
4954
## 0.28.2 -- 2023-04-12
5055

5156
### New Features
@@ -86,8 +91,8 @@
8691
to trim leading and trailing spaces from text events
8792
- [#565]: Allow deserialize special field names `$value` and `$text` into borrowed
8893
fields when use serde deserializer
89-
- [#568]: Rename `Writter::inner` into `Writter::get_mut`
90-
- [#568]: Add method `Writter::get_ref`
94+
- [#568]: Rename `Writer::inner` into `Writer::get_mut`
95+
- [#568]: Add method `Writer::get_ref`
9196
- [#569]: Rewrite the `Reader::read_event_into_async` as an async fn, making the future `Send` if possible.
9297
- [#571]: Borrow element names (`<element>`) when deserialize with serde.
9398
This change allow to deserialize into `HashMap<&str, T>`, for example

src/se/mod.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -529,7 +529,12 @@ impl<'w, 'r, W: Write> Serializer<'w, 'r, W> {
529529

530530
/// Configure indent for a serializer
531531
pub fn indent(&mut self, indent_char: char, indent_size: usize) -> &mut Self {
532-
self.ser.indent = Indent::Owned(Indentation::new(indent_char as u8, indent_size));
532+
self.set_indent(Indentation::new(indent_char as u8, indent_size))
533+
}
534+
535+
/// Configure indent object for a serializer
536+
pub(crate) fn set_indent(&mut self, indent: Indentation) -> &mut Self {
537+
self.ser.indent = Indent::Owned(indent);
533538
self
534539
}
535540

src/writer.rs

Lines changed: 159 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ use crate::events::{attributes::Attribute, BytesCData, BytesStart, BytesText, Ev
1010
mod async_tokio;
1111

1212
/// XML writer. Writes XML [`Event`]s to a [`std::io::Write`] or [`tokio::io::AsyncWrite`] implementor.
13+
#[cfg(feature = "serialize")]
14+
use {crate::de::DeError, serde::Serialize};
15+
16+
/// XML writer. Writes XML [`Event`]s to a [`std::io::Write`] implementor.
1317
///
1418
/// # Examples
1519
///
@@ -261,6 +265,71 @@ impl<W: Write> Writer<W> {
261265
start_tag: BytesStart::new(name.as_ref()),
262266
}
263267
}
268+
269+
/// Write an arbitrary serializable object
270+
///
271+
/// ```rust
272+
/// # use pretty_assertions::assert_eq;
273+
/// # use serde::Serialize;
274+
/// # use quick_xml::events::{BytesStart, Event};
275+
/// # use quick_xml::writer::Writer;
276+
/// # use quick_xml::DeError;
277+
/// # fn main() -> Result<(), DeError> {
278+
///
279+
/// #[derive(Debug, PartialEq, Serialize)]
280+
/// struct MyData {
281+
/// question: String,
282+
/// answer: u32,
283+
/// }
284+
///
285+
/// let data = MyData {
286+
/// question: "The Ultimate Question of Life, the Universe, and Everything".into(),
287+
/// answer: 42,
288+
/// };
289+
///
290+
/// let mut buffer = Vec::new();
291+
/// let mut writer = Writer::new_with_indent(&mut buffer, b' ', 4);
292+
///
293+
/// let start = BytesStart::new("root");
294+
/// let end = start.to_end();
295+
///
296+
/// writer.write_event(Event::Start(start.clone()))?;
297+
/// writer.write_serializable("my_data", &data)?;
298+
/// writer.write_event(Event::End(end))?;
299+
///
300+
/// assert_eq!(
301+
/// std::str::from_utf8(&buffer)?,
302+
/// r#"<root>
303+
/// <my_data>
304+
/// <question>The Ultimate Question of Life, the Universe, and Everything</question>
305+
/// <answer>42</answer>
306+
/// </my_data>
307+
/// </root>"#
308+
/// );
309+
/// # Ok(())
310+
/// # }
311+
/// ```
312+
#[cfg(feature = "serialize")]
313+
pub fn write_serializable<T: Serialize>(
314+
&mut self,
315+
tag_name: &str,
316+
content: &T,
317+
) -> std::result::Result<(), DeError> {
318+
use crate::se::Serializer;
319+
320+
self.write_indent()?;
321+
let indent = self.indent.clone();
322+
let mut fmt = ToFmtWrite(self.get_mut());
323+
let mut serializer = Serializer::with_root(&mut fmt, Some(tag_name))?;
324+
325+
if let Some(indent) = indent {
326+
serializer.set_indent(indent);
327+
}
328+
329+
content.serialize(serializer)?;
330+
331+
Ok(())
332+
}
264333
}
265334

266335
/// A struct to write an element. Contains methods to add attributes and inner
@@ -341,14 +410,31 @@ impl<'a, W: Write> ElementWriter<'a, W> {
341410
Ok(self.writer)
342411
}
343412
}
413+
#[cfg(feature = "serialize")]
414+
struct ToFmtWrite<T>(pub T);
415+
416+
#[cfg(feature = "serialize")]
417+
impl<T> std::fmt::Write for ToFmtWrite<T>
418+
where
419+
T: std::io::Write,
420+
{
421+
fn write_str(&mut self, s: &str) -> std::fmt::Result {
422+
self.0.write_all(s.as_bytes()).map_err(|_| std::fmt::Error)
423+
}
424+
}
344425

345426
#[derive(Clone)]
346427
pub(crate) struct Indentation {
428+
/// todo: does this even belong here? It has no impact on indentation logic.
347429
should_line_break: bool,
430+
/// The character code to be used for indentations (e.g. ` ` or `\t`)
348431
indent_char: u8,
432+
/// How many instances of the indent character ought to be used for each level of indentation
349433
indent_size: usize,
434+
/// Used as a cache for the bytes used for indentation
350435
indents: Vec<u8>,
351-
indents_len: usize,
436+
/// The current amount of indentation
437+
current_indent_len: usize,
352438
}
353439

354440
impl Indentation {
@@ -358,26 +444,27 @@ impl Indentation {
358444
indent_char,
359445
indent_size,
360446
indents: vec![indent_char; 128],
361-
indents_len: 0,
447+
current_indent_len: 0, // invariant - needs to remain less than indents.len()
362448
}
363449
}
364450

365451
/// Increase indentation by one level
366452
pub fn grow(&mut self) {
367-
self.indents_len += self.indent_size;
368-
if self.indents_len > self.indents.len() {
369-
self.indents.resize(self.indents_len, self.indent_char);
453+
self.current_indent_len += self.indent_size;
454+
if self.current_indent_len > self.indents.len() {
455+
self.indents
456+
.resize(self.current_indent_len, self.indent_char);
370457
}
371458
}
372459

373460
/// Decrease indentation by one level. Do nothing, if level already zero
374461
pub fn shrink(&mut self) {
375-
self.indents_len = self.indents_len.saturating_sub(self.indent_size);
462+
self.current_indent_len = self.current_indent_len.saturating_sub(self.indent_size);
376463
}
377464

378465
/// Returns indent string for current level
379466
pub fn current(&self) -> &[u8] {
380-
&self.indents[..self.indents_len]
467+
&self.indents[..self.current_indent_len]
381468
}
382469
}
383470

@@ -547,6 +634,71 @@ mod indentation {
547634
);
548635
}
549636

637+
#[cfg(feature = "serialize")]
638+
#[test]
639+
fn serializable() {
640+
#[derive(Serialize)]
641+
struct Foo {
642+
#[serde(rename = "@attribute")]
643+
attribute: &'static str,
644+
645+
element: Bar,
646+
list: Vec<&'static str>,
647+
648+
#[serde(rename = "$text")]
649+
text: &'static str,
650+
651+
val: String,
652+
}
653+
654+
#[derive(Serialize)]
655+
struct Bar {
656+
baz: usize,
657+
bat: usize,
658+
}
659+
660+
let mut buffer = Vec::new();
661+
let mut writer = Writer::new_with_indent(&mut buffer, b' ', 4);
662+
663+
let content = Foo {
664+
attribute: "attribute",
665+
element: Bar { baz: 42, bat: 43 },
666+
list: vec!["first element", "second element"],
667+
text: "text",
668+
val: "foo".to_owned(),
669+
};
670+
671+
let start = BytesStart::new("paired")
672+
.with_attributes(vec![("attr1", "value1"), ("attr2", "value2")].into_iter());
673+
let end = start.to_end();
674+
675+
writer
676+
.write_event(Event::Start(start.clone()))
677+
.expect("write start tag failed");
678+
writer
679+
.write_serializable("foo_element", &content)
680+
.expect("write serializable inner contents failed");
681+
writer
682+
.write_event(Event::End(end))
683+
.expect("write end tag failed");
684+
685+
assert_eq!(
686+
std::str::from_utf8(&buffer).unwrap(),
687+
r#"<paired attr1="value1" attr2="value2">
688+
<foo_element attribute="attribute">
689+
<element>
690+
<baz>42</baz>
691+
<bat>43</bat>
692+
</element>
693+
<list>first element</list>
694+
<list>second element</list>
695+
text
696+
<val>foo</val>
697+
</foo_element>
698+
</paired>"#
699+
);
700+
}
701+
550702
#[test]
551703
fn element_writer_empty() {
552704
let mut buffer = Vec::new();

0 commit comments

Comments
 (0)