diff --git a/src/de/mod.rs b/src/de/mod.rs
index a60f49b4..6ee04fa4 100644
--- a/src/de/mod.rs
+++ b/src/de/mod.rs
@@ -1577,15 +1577,24 @@
//! uses that name. This will allow you to switch XML crates more smoothly if required.
//!
//!
-//! Representation of primitive types in `$value` does not differ from their
-//! representation in `$text` field. The difference is how sequences are serialized.
-//! `$value` serializes each sequence item as a separate XML element. The name
-//! of that element is taken from serialized type, and because only `enum`s provide
-//! such name (their variant name), only they should be used for such fields.
-//!
-//! `$value` fields does not support `struct` types with fields, the serialization
-//! of such types would end with an `Err(Unsupported)`. Unit structs and unit
-//! type `()` serializing to nothing and can be deserialized from any content.
+//! The representation of primitive types in `$value` does not differ from their
+//! representation in `$text` fields. The difference is how sequences are serialized
+//! and deserialized. `$value` serializes each sequence item as a separate XML element.
+//! How the name of the XML element is chosen depends on the field's type. For
+//! `enum`s, the variant name is used. For `struct`s, the name of the `struct`
+//! is used.
+//!
+//! During deserialization, if the `$value` field is an enum, then the variant's
+//! name is matched against. That's **not** the case with structs, however, since
+//! `serde` does not expose type names of nested fields. This does mean that **any**
+//! type could be deserialized into a `$value` struct-type field, so long as the
+//! struct's fields have compatible types (or are captured as text by `String`
+//! or similar-behaving types). This can be handy when using generic types in fields
+//! where one knows in advance what to expect. If you do not know what to expect,
+//! however, prefer an enum with all possible variants.
+//!
+//! Unit structs and unit type `()` serialize to nothing and can be deserialized
+//! from any content.
//!
//! Serialization and deserialization of `$value` field performed as usual, except
//! that name for an XML element will be given by the serialized type, instead of
@@ -1646,6 +1655,38 @@
//! # );
//! ```
//!
+//! The next example demonstrates how generic types can be used in conjunction
+//! with `$value`-named fields to allow the reuse of wrapping structs. A common
+//! example use case for this feature is SOAP messages, which can be commmonly
+//! found wrapped around ` ... `.
+//!
+//! ```rust
+//! # use serde::Serialize;
+//! # use pretty_assertions::assert_eq;
+//! #
+//! #[derive(Serialize)]
+//! struct Envelope {
+//! body: Body,
+//! }
+//!
+//! #[derive(Serialize)]
+//! struct Body {
+//! #[serde(rename = "$value")]
+//! inner: T,
+//! }
+//!
+//! #[derive(Serialize)]
+//! struct Example {
+//! a: i32,
+//! }
+//!
+//! assert_eq!(
+//! quick_xml::se::to_string(&Envelope { body: Body { inner: Example { a: 42 } } }).unwrap(),
+//! // Notice how `inner` is not present in the XML
+//! "42",
+//! );
+//! ```
+//!
//! ### Primitives and sequences of primitives
//!
//! Sequences serialized to a list of elements. Note, that types that does not
diff --git a/src/se/content.rs b/src/se/content.rs
index 46c6170f..0cc45988 100644
--- a/src/se/content.rs
+++ b/src/se/content.rs
@@ -179,7 +179,7 @@ impl<'w, 'i, W: Write> Serializer for ContentSerializer<'w, 'i, W> {
type SerializeTupleStruct = Seq<'w, 'i, W>;
type SerializeTupleVariant = Tuple<'w, 'i, W>;
type SerializeMap = Impossible;
- type SerializeStruct = Impossible;
+ type SerializeStruct = Struct<'w, 'i, W>;
type SerializeStructVariant = Struct<'w, 'i, W>;
write_primitive!(serialize_bool(bool));
@@ -352,11 +352,13 @@ impl<'w, 'i, W: Write> Serializer for ContentSerializer<'w, 'i, W> {
fn serialize_struct(
self,
name: &'static str,
- _len: usize,
+ len: usize,
) -> Result {
- Err(SeError::Unsupported(
- format!("serialization of struct `{name}` is not supported in `$value` field").into(),
- ))
+ ElementSerializer {
+ ser: self,
+ key: XmlName::try_from(name)?,
+ }
+ .serialize_struct(name, len)
}
/// Serializes variant as an element with name `variant`, producing
@@ -707,8 +709,13 @@ pub(super) mod tests {
// only `enum` can provide
err!(map: BTreeMap::from([("_1", 2), ("_3", 4)])
=> Unsupported("serialization of map types is not supported in `$value` field"));
- err!(struct_: Struct { key: "answer", val: (42, 42) }
- => Unsupported("serialization of struct `Struct` is not supported in `$value` field"));
+ serialize_as!(struct_: Struct { key: "answer", val: (42, 42) }
+ => "\
+ answer\
+ 42\
+ 42\
+ ");
+
serialize_as!(enum_struct: Enum::Struct { key: "answer", val: (42, 42) }
=> "\
answer\
@@ -723,13 +730,17 @@ pub(super) mod tests {
err!(map: BTreeMap::from([("$text", 2), ("_3", 4)])
=> Unsupported("serialization of map types is not supported in `$value` field"));
- err!(struct_:
+ serialize_as!(struct_:
Text {
before: "answer",
content: (42, 42),
after: "answer",
}
- => Unsupported("serialization of struct `Text` is not supported in `$value` field"));
+ => "\
+ answer\
+ 42 42\
+ answer\
+ ");
serialize_as!(enum_struct:
SpecialEnum::Text {
before: "answer",
@@ -987,13 +998,21 @@ pub(super) mod tests {
after: "answer",
}
=> Unsupported("serialization of map types is not supported in `$value` field"));
- err!(struct_:
+ value!(struct_:
SpecialEnum::Value {
before: "answer",
content: Struct { key: "answer", val: (42, 42) },
after: "answer",
}
- => Unsupported("serialization of struct `Struct` is not supported in `$value` field"));
+ => "\
+ answer\
+ \
+ answer\
+ 42\
+ 42\
+ \
+ answer\
+ ");
value!(enum_struct:
Enum::Struct { key: "answer", val: (42, 42) }
=> "\
@@ -1012,12 +1031,12 @@ pub(super) mod tests {
err!(map_mixed: BTreeMap::from([("@key1", 1), ("key2", 2)])
=> Unsupported("serialization of map types is not supported in `$value` field"));
- err!(struct_: Attributes { key: "answer", val: (42, 42) }
- => Unsupported("serialization of struct `Attributes` is not supported in `$value` field"));
- err!(struct_before: AttributesBefore { key: "answer", val: 42 }
- => Unsupported("serialization of struct `AttributesBefore` is not supported in `$value` field"));
- err!(struct_after: AttributesAfter { key: "answer", val: 42 }
- => Unsupported("serialization of struct `AttributesAfter` is not supported in `$value` field"));
+ serialize_as!(struct_: Attributes { key: "answer", val: (42, 42) }
+ => r#""#);
+ serialize_as!(struct_before: AttributesBefore { key: "answer", val: 42 }
+ => r#"42"#);
+ serialize_as!(struct_after: AttributesAfter { key: "answer", val: 42 }
+ => r#"answer"#);
serialize_as!(enum_: Enum::Attributes { key: "answer", val: (42, 42) }
=> r#""#);
@@ -1157,8 +1176,12 @@ pub(super) mod tests {
// only `enum` can provide
err!(map: BTreeMap::from([("_1", 2), ("_3", 4)])
=> Unsupported("serialization of map types is not supported in `$value` field"));
- err!(struct_: Struct { key: "answer", val: (42, 42) }
- => Unsupported("serialization of struct `Struct` is not supported in `$value` field"));
+ serialize_as!(struct_: Struct { key: "answer", val: (42, 42) }
+ => "\n \
+ answer\n \
+ 42\n \
+ 42\n\
+ ");
serialize_as!(enum_struct: Enum::Struct { key: "answer", val: (42, 42) }
=> "\n \
answer\n \
@@ -1173,13 +1196,15 @@ pub(super) mod tests {
err!(map: BTreeMap::from([("$text", 2), ("_3", 4)])
=> Unsupported("serialization of map types is not supported in `$value` field"));
- err!(struct_:
+ serialize_as!(struct_:
Text {
before: "answer",
content: (42, 42),
after: "answer",
}
- => Unsupported("serialization of struct `Text` is not supported in `$value` field"));
+ => "\n \
+ answer42 42answer\n\
+ ");
serialize_as!(enum_struct:
SpecialEnum::Text {
before: "answer",
@@ -1436,13 +1461,22 @@ pub(super) mod tests {
after: "answer",
}
=> Unsupported("serialization of map types is not supported in `$value` field"));
- err!(struct_:
+ value!(struct_:
SpecialEnum::Value {
before: "answer",
content: Struct { key: "answer", val: (42, 42) },
after: "answer",
}
- => Unsupported("serialization of struct `Struct` is not supported in `$value` field"));
+ => "\n \
+ \n \
+ answer\n \
+ \n \
+ answer\n \
+ 42\n \
+ 42\n \
+ \n \
+ answer\n \
+ \n ");
value!(enum_struct:
Enum::Struct { key: "answer", val: (42, 42) }
=> "\n \
@@ -1462,12 +1496,16 @@ pub(super) mod tests {
err!(map_mixed: BTreeMap::from([("@key1", 1), ("key2", 2)])
=> Unsupported("serialization of map types is not supported in `$value` field"));
- err!(struct_: Attributes { key: "answer", val: (42, 42) }
- => Unsupported("serialization of struct `Attributes` is not supported in `$value` field"));
- err!(struct_before: AttributesBefore { key: "answer", val: 42 }
- => Unsupported("serialization of struct `AttributesBefore` is not supported in `$value` field"));
- err!(struct_after: AttributesAfter { key: "answer", val: 42 }
- => Unsupported("serialization of struct `AttributesAfter` is not supported in `$value` field"));
+ serialize_as!(struct_: Attributes { key: "answer", val: (42, 42) }
+ => r#""#);
+ serialize_as!(struct_before: AttributesBefore { key: "answer", val: 42 }
+ => "\n \
+ 42\n\
+ ");
+ serialize_as!(struct_after: AttributesAfter { key: "answer", val: 42 }
+ => "\n \
+ answer\n\
+ ");
serialize_as!(enum_: Enum::Attributes { key: "answer", val: (42, 42) }
=> r#""#);
diff --git a/src/se/element.rs b/src/se/element.rs
index 2f6d5f30..f9c43093 100644
--- a/src/se/element.rs
+++ b/src/se/element.rs
@@ -1107,14 +1107,18 @@ mod tests {
=> "first\
42");
- // We cannot wrap map or struct in any container and should not
- // flatten it, so it is impossible to serialize maps and structs
+ // We cannot wrap map in any container and should not
+ // flatten it, so it is impossible to serialize maps
err!(map:
BTreeMap::from([("$value", BTreeMap::from([("_1", 2), ("_3", 4)]))])
=> Unsupported("serialization of map types is not supported in `$value` field"));
- err!(struct_:
- BTreeMap::from([("$value", Struct { key: "answer", val: (42, 42) })])
- => Unsupported("serialization of struct `Struct` is not supported in `$value` field"));
+ value!(struct_:
+ Struct { key: "answer", val: (42, 42) }
+ => "\
+ answer\
+ 42\
+ 42\
+ ");
value!(enum_struct:
Enum::Struct { key: "answer", val: (42, 42) }
=> "\
@@ -1234,8 +1238,8 @@ mod tests {
=> "first\
42");
- // We cannot wrap map or struct in any container and should not
- // flatten it, so it is impossible to serialize maps and structs
+ // We cannot wrap map in any container and should not
+ // flatten it, so it is impossible to serialize maps
err!(map:
Value {
before: "answer",
@@ -1243,13 +1247,13 @@ mod tests {
after: "answer",
}
=> Unsupported("serialization of map types is not supported in `$value` field"));
- err!(struct_:
- Value {
- before: "answer",
- content: Struct { key: "answer", val: (42, 42) },
- after: "answer",
- }
- => Unsupported("serialization of struct `Struct` is not supported in `$value` field"));
+ value!(struct_:
+ Struct { key: "answer", val: (42, 42) }
+ => "\
+ answer\
+ 42\
+ 42\
+ ");
value!(enum_struct:
Enum::Struct { key: "answer", val: (42, 42) }
=> "\
@@ -1830,14 +1834,19 @@ mod tests {
first\n \
42\n");
- // We cannot wrap map or struct in any container and should not
- // flatten it, so it is impossible to serialize maps and structs
+ // We cannot wrap map in any container and should not
+ // flatten it, so it is impossible to serialize maps
err!(map:
BTreeMap::from([("$value", BTreeMap::from([("_1", 2), ("_3", 4)]))])
=> Unsupported("serialization of map types is not supported in `$value` field"));
- err!(struct_:
- BTreeMap::from([("$value", Struct { key: "answer", val: (42, 42) })])
- => Unsupported("serialization of struct `Struct` is not supported in `$value` field"));
+ value!(struct_:
+ Struct { key: "answer", val: (42, 42) }
+ => "\n \
+ \n \
+ answer\n \
+ 42\n \
+ 42\n \
+ \n");
value!(enum_struct:
Enum::Struct { key: "answer", val: (42, 42) }
=> "\n \
@@ -1959,8 +1968,8 @@ mod tests {
first\n \
42\n ");
- // We cannot wrap map or struct in any container and should not
- // flatten it, so it is impossible to serialize maps and structs
+ // We cannot wrap map in any container and should not
+ // flatten it, so it is impossible to serialize maps
err!(map:
Value {
before: "answer",
@@ -1968,13 +1977,22 @@ mod tests {
after: "answer",
}
=> Unsupported("serialization of map types is not supported in `$value` field"));
- err!(struct_:
+ value!(struct_:
Value {
before: "answer",
content: Struct { key: "answer", val: (42, 42) },
after: "answer",
}
- => Unsupported("serialization of struct `Struct` is not supported in `$value` field"));
+ => "\n \
+ \n \
+ answer\n \
+ \n \
+ answer\n \
+ 42\n \
+ 42\n \
+ \n \
+ answer\n \
+ \n ");
value!(enum_struct:
Enum::Struct { key: "answer", val: (42, 42) }
=> "\n \