Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add helper functions to_*_with_root that writes XML with the specified root tag name #556

Merged
merged 1 commit into from
Feb 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@

- [#541]: Deserialize specially named `$text` enum variant in [externally tagged]
enums from textual content
- [#556]: `to_writer` and `to_string` now accept `?Sized` types
- [#556]: Add new `to_writer_with_root` and `to_string_with_root` helper functions

### Bug Fixes

Expand All @@ -29,6 +31,7 @@
[#510]: https://github.com/tafia/quick-xml/issues/510
[#537]: https://github.com/tafia/quick-xml/issues/537
[#541]: https://github.com/tafia/quick-xml/pull/541
[#556]: https://github.com/tafia/quick-xml/pull/556

## 0.27.1 -- 2022-12-28

Expand Down
162 changes: 158 additions & 4 deletions src/se/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,16 +87,170 @@ use serde::serde_if_integer128;
use std::fmt::Write;
use std::str::from_utf8;

/// Serialize struct into a `Write`r
pub fn to_writer<W: Write, S: Serialize>(writer: W, value: &S) -> Result<W, DeError> {
/// Serialize struct into a `Write`r.
///
/// # Examples
///
/// ```
/// # use quick_xml::se::to_writer;
/// # use serde::Serialize;
/// # use pretty_assertions::assert_eq;
/// #[derive(Serialize)]
/// struct Root<'a> {
/// #[serde(rename = "@attribute")]
/// attribute: &'a str,
/// element: &'a str,
/// #[serde(rename = "$text")]
/// text: &'a str,
/// }
///
/// let data = Root {
/// attribute: "attribute content",
/// element: "element content",
/// text: "text content",
/// };
///
/// assert_eq!(
/// to_writer(String::new(), &data).unwrap(),
/// // The root tag name is automatically deduced from the struct name
/// // This will not work for other types or struct with #[serde(flatten)] fields
/// "<Root attribute=\"attribute content\">\
/// <element>element content</element>\
/// text content\
/// </Root>"
/// );
/// ```
pub fn to_writer<W, T>(writer: W, value: &T) -> Result<W, DeError>
where
W: Write,
Copy link
Collaborator

@dralley dralley Feb 19, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer that in cases where we use std::fmt::Write, we do so explicitly by using fmt::Write. Otherwise it is too easy to confuse [fmt::]Write for [io::]Write.

Copy link
Collaborator

@dralley dralley Feb 19, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the same vein it feels as though to_writer() may be ambiguous to some users as this is a common function name with respect to io::Write, however I admit that I cannot think of any better name, so it's OK.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changing Write to fmt::Write does not change the generated documentation (which show trait as Write), so this change does not give much effect. Because we use only one Write in the serialization modules, I think, that using simple name will not confuse developers.

T: ?Sized + Serialize,
{
value.serialize(Serializer::new(writer))
}

/// Serialize struct into a `String`
pub fn to_string<S: Serialize>(value: &S) -> Result<String, DeError> {
/// Serialize struct into a `String`.
///
/// # Examples
///
/// ```
/// # use quick_xml::se::to_string;
/// # use serde::Serialize;
/// # use pretty_assertions::assert_eq;
/// #[derive(Serialize)]
/// struct Root<'a> {
/// #[serde(rename = "@attribute")]
/// attribute: &'a str,
/// element: &'a str,
/// #[serde(rename = "$text")]
/// text: &'a str,
/// }
///
/// let data = Root {
/// attribute: "attribute content",
/// element: "element content",
/// text: "text content",
/// };
///
/// assert_eq!(
/// to_string(&data).unwrap(),
/// // The root tag name is automatically deduced from the struct name
/// // This will not work for other types or struct with #[serde(flatten)] fields
/// "<Root attribute=\"attribute content\">\
/// <element>element content</element>\
/// text content\
/// </Root>"
/// );
/// ```
pub fn to_string<T>(value: &T) -> Result<String, DeError>
where
T: ?Sized + Serialize,
{
to_writer(String::new(), value)
}

/// Serialize struct into a `Write`r using specified root tag name.
/// `root_tag` should be valid [XML name], otherwise error is returned.
///
/// # Examples
///
/// ```
/// # use quick_xml::se::to_writer_with_root;
/// # use serde::Serialize;
/// # use pretty_assertions::assert_eq;
/// #[derive(Serialize)]
/// struct Root<'a> {
/// #[serde(rename = "@attribute")]
/// attribute: &'a str,
/// element: &'a str,
/// #[serde(rename = "$text")]
/// text: &'a str,
/// }
///
/// let data = Root {
/// attribute: "attribute content",
/// element: "element content",
/// text: "text content",
/// };
///
/// assert_eq!(
/// to_writer_with_root(String::new(), "top-level", &data).unwrap(),
/// "<top-level attribute=\"attribute content\">\
/// <element>element content</element>\
/// text content\
/// </top-level>"
/// );
/// ```
///
/// [XML name]: https://www.w3.org/TR/REC-xml/#NT-Name
pub fn to_writer_with_root<W, T>(writer: W, root_tag: &str, value: &T) -> Result<W, DeError>
where
W: Write,
T: ?Sized + Serialize,
{
value.serialize(Serializer::with_root(writer, Some(root_tag))?)
}

/// Serialize struct into a `String` using specified root tag name.
/// `root_tag` should be valid [XML name], otherwise error is returned.
///
/// # Examples
///
/// ```
/// # use quick_xml::se::to_string_with_root;
/// # use serde::Serialize;
/// # use pretty_assertions::assert_eq;
/// #[derive(Serialize)]
/// struct Root<'a> {
/// #[serde(rename = "@attribute")]
/// attribute: &'a str,
/// element: &'a str,
/// #[serde(rename = "$text")]
/// text: &'a str,
/// }
///
/// let data = Root {
/// attribute: "attribute content",
/// element: "element content",
/// text: "text content",
/// };
///
/// assert_eq!(
/// to_string_with_root("top-level", &data).unwrap(),
/// "<top-level attribute=\"attribute content\">\
/// <element>element content</element>\
/// text content\
/// </top-level>"
/// );
/// ```
///
/// [XML name]: https://www.w3.org/TR/REC-xml/#NT-Name
pub fn to_string_with_root<T>(root_tag: &str, value: &T) -> Result<String, DeError>
where
T: ?Sized + Serialize,
{
to_writer_with_root(String::new(), root_tag, value)
}

////////////////////////////////////////////////////////////////////////////////////////////////////

/// Defines which characters would be escaped in [`Text`] events and attribute
Expand Down