Skip to content

Commit fe20b46

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 fe20b46

File tree

6 files changed

+150
-19
lines changed

6 files changed

+150
-19
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 `write_serializable_content` 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/content.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -670,7 +670,7 @@ pub(super) mod tests {
670670
let ser = ContentSerializer {
671671
writer: &mut buffer,
672672
level: QuoteLevel::Full,
673-
indent: Indent::Owned(Indentation::new(b' ', 2)),
673+
indent: Indent::Owned(Indentation::new(b' ', 2, 0)),
674674
write_indent: false,
675675
};
676676

@@ -690,7 +690,7 @@ pub(super) mod tests {
690690
let ser = ContentSerializer {
691691
writer: &mut buffer,
692692
level: QuoteLevel::Full,
693-
indent: Indent::Owned(Indentation::new(b' ', 2)),
693+
indent: Indent::Owned(Indentation::new(b' ', 2, 0)),
694694
write_indent: false,
695695
};
696696

src/se/element.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1531,7 +1531,7 @@ mod tests {
15311531
ser: ContentSerializer {
15321532
writer: &mut buffer,
15331533
level: QuoteLevel::Full,
1534-
indent: Indent::Owned(Indentation::new(b' ', 2)),
1534+
indent: Indent::Owned(Indentation::new(b' ', 2, 0)),
15351535
write_indent: false,
15361536
},
15371537
key: XmlName("root"),
@@ -1554,7 +1554,7 @@ mod tests {
15541554
ser: ContentSerializer {
15551555
writer: &mut buffer,
15561556
level: QuoteLevel::Full,
1557-
indent: Indent::Owned(Indentation::new(b' ', 2)),
1557+
indent: Indent::Owned(Indentation::new(b' ', 2, 0)),
15581558
write_indent: false,
15591559
},
15601560
key: XmlName("root"),

src/se/mod.rs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -528,8 +528,19 @@ impl<'w, 'r, W: Write> Serializer<'w, 'r, W> {
528528
}
529529

530530
/// Configure indent for a serializer
531-
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));
531+
///
532+
/// Note: `initial_indent_amt` is the initial total length of the indent, not the indent level
533+
pub fn indent(
534+
&mut self,
535+
indent_char: char,
536+
indent_size: usize,
537+
initial_indent_amt: usize,
538+
) -> &mut Self {
539+
self.ser.indent = Indent::Owned(Indentation::new(
540+
indent_char as u8,
541+
indent_size,
542+
initial_indent_amt,
543+
));
533544
self
534545
}
535546

src/writer.rs

Lines changed: 124 additions & 9 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
///
@@ -75,7 +79,7 @@ impl<W> Writer<W> {
7579
pub fn new_with_indent(inner: W, indent_char: u8, indent_size: usize) -> Writer<W> {
7680
Writer {
7781
writer: inner,
78-
indent: Some(Indentation::new(indent_char, indent_size)),
82+
indent: Some(Indentation::new(indent_char, indent_size, 0)),
7983
}
8084
}
8185

@@ -261,6 +265,33 @@ impl<W: Write> Writer<W> {
261265
start_tag: BytesStart::new(name.as_ref()),
262266
}
263267
}
268+
269+
/// Write an arbitrary serializable object
270+
#[cfg(feature = "serialize")]
271+
pub fn write_serializable<T: Serialize>(
272+
&mut self,
273+
content: &T,
274+
tag_name: &str,
275+
) -> std::result::Result<(), DeError> {
276+
use crate::se::Serializer;
277+
278+
self.write_indent()?;
279+
let indent = self.indent.clone();
280+
let mut fmt = ToFmtWrite(self.get_mut());
281+
let mut serializer = Serializer::with_root(&mut fmt, Some(tag_name))?;
282+
283+
if let Some(indent) = indent {
284+
serializer.indent(
285+
indent.indent_char as char,
286+
indent.indent_size,
287+
indent.current_indent_len,
288+
);
289+
}
290+
291+
content.serialize(serializer)?;
292+
293+
Ok(())
294+
}
264295
}
265296

266297
/// A struct to write an element. Contains methods to add attributes and inner
@@ -341,43 +372,62 @@ impl<'a, W: Write> ElementWriter<'a, W> {
341372
Ok(self.writer)
342373
}
343374
}
375+
#[cfg(feature = "serialize")]
376+
struct ToFmtWrite<T>(pub T);
377+
378+
#[cfg(feature = "serialize")]
379+
impl<T> std::fmt::Write for ToFmtWrite<T>
380+
where
381+
T: std::io::Write,
382+
{
383+
fn write_str(&mut self, s: &str) -> std::fmt::Result {
384+
self.0.write_all(s.as_bytes()).map_err(|_| std::fmt::Error)
385+
}
386+
}
344387

345388
#[derive(Clone)]
346389
pub(crate) struct Indentation {
390+
/// todo: does this even belong here? It has no impact on indentation logic.
347391
should_line_break: bool,
392+
/// The character code to be used for indentations (e.g. ` ` or `\t`)
348393
indent_char: u8,
394+
/// How many instances of the indent character ought to be used for each level of indentation
349395
indent_size: usize,
396+
/// Used as a cache for the bytes used for indentation
350397
indents: Vec<u8>,
351-
indents_len: usize,
398+
/// The current amount of indentation - must be less than indents.len()
399+
current_indent_len: usize,
352400
}
353401

354402
impl Indentation {
355-
pub fn new(indent_char: u8, indent_size: usize) -> Self {
403+
pub fn new(indent_char: u8, indent_size: usize, indents_len: usize) -> Self {
356404
Self {
357405
should_line_break: false,
358406
indent_char,
359407
indent_size,
360408
indents: vec![indent_char; 128],
361-
indents_len: 0,
409+
current_indent_len: indents_len,
362410
}
363411
}
364412

365413
/// Increase indentation by one level
366414
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);
415+
self.current_indent_len += self.indent_size;
416+
if self.current_indent_len > self.indents.len() {
417+
self.indents
418+
.resize(self.current_indent_len, self.indent_char);
370419
}
371420
}
372421

373422
/// Decrease indentation by one level. Do nothing, if level already zero
374423
pub fn shrink(&mut self) {
375-
self.indents_len = self.indents_len.saturating_sub(self.indent_size);
424+
self.current_indent_len = self.current_indent_len.saturating_sub(self.indent_size);
376425
}
377426

378427
/// Returns indent string for current level
379428
pub fn current(&self) -> &[u8] {
380-
&self.indents[..self.indents_len]
429+
// todo: might be buggy if initialized > 128 without a call to grow() first
430+
&self.indents[..self.current_indent_len]
381431
}
382432
}
383433

@@ -547,6 +597,71 @@ mod indentation {
547597
);
548598
}
549599

600+
#[cfg(feature = "serialize")]
601+
#[test]
602+
fn serializable() {
603+
#[derive(Serialize)]
604+
struct Foo {
605+
#[serde(rename = "@attribute")]
606+
attribute: &'static str,
607+
608+
element: Bar,
609+
list: Vec<&'static str>,
610+
611+
#[serde(rename = "$text")]
612+
text: &'static str,
613+
614+
val: String,
615+
}
616+
617+
#[derive(Serialize)]
618+
struct Bar {
619+
baz: usize,
620+
bat: usize,
621+
}
622+
623+
let mut buffer = Vec::new();
624+
let mut writer = Writer::new_with_indent(&mut buffer, b' ', 4);
625+
626+
let content = Foo {
627+
attribute: "attribute",
628+
element: Bar { baz: 42, bat: 43 },
629+
list: vec!["first element", "second element"],
630+
text: "text",
631+
val: "foo".to_owned(),
632+
};
633+
634+
let start = BytesStart::new("paired")
635+
.with_attributes(vec![("attr1", "value1"), ("attr2", "value2")].into_iter());
636+
let end = start.to_end();
637+
638+
writer
639+
.write_event(Event::Start(start.clone()))
640+
.expect("write start tag failed");
641+
writer
642+
.write_serializable(&content, "foo_element")
643+
.expect("write serializable inner contents failed");
644+
writer
645+
.write_event(Event::End(end))
646+
.expect("write end tag failed");
647+
648+
assert_eq!(
649+
std::str::from_utf8(&buffer).unwrap(),
650+
r#"<paired attr1="value1" attr2="value2">
651+
<foo_element attribute="attribute">
652+
<element>
653+
<baz>42</baz>
654+
<bat>43</bat>
655+
</element>
656+
<list>first element</list>
657+
<list>second element</list>
658+
text
659+
<val>foo</val>
660+
</foo_element>
661+
</paired>"#
662+
);
663+
}
664+
550665
#[test]
551666
fn element_writer_empty() {
552667
let mut buffer = Vec::new();

tests/serde-se.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -699,7 +699,7 @@ mod without_root {
699699
fn $name() {
700700
let mut buffer = String::new();
701701
let mut ser = Serializer::new(&mut buffer);
702-
ser.indent(' ', 2);
702+
ser.indent(' ', 2, 0);
703703

704704
$data.serialize(ser).unwrap();
705705
assert_eq!(buffer, $expected);

0 commit comments

Comments
 (0)