Skip to content

Commit 69631c2

Browse files
SyndelisMingun
authored andcommitted
feat: support serializing structs into $value named fields
1 parent a19d4a1 commit 69631c2

File tree

4 files changed

+174
-61
lines changed

4 files changed

+174
-61
lines changed

Changelog.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,14 @@
2020
- `Deserializer::borrowing_with_resolver`
2121
- `Deserializer::buffering`
2222
- `Deserializer::buffering_with_resolver`
23+
- [#878]: Add ability to serialize structs in `$value` fields. The struct name will
24+
be used as a tag name. Previously only enums was allowed there.
2325

2426
### Bug Fixes
2527

2628
### Misc Changes
2729

30+
[#878]: https://github.com/tafia/quick-xml/pull/878
2831
[#882]: https://github.com/tafia/quick-xml/pull/882
2932

3033

src/de/mod.rs

Lines changed: 63 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1577,15 +1577,24 @@
15771577
//! uses that name. This will allow you to switch XML crates more smoothly if required.
15781578
//! </div>
15791579
//!
1580-
//! Representation of primitive types in `$value` does not differ from their
1581-
//! representation in `$text` field. The difference is how sequences are serialized.
1582-
//! `$value` serializes each sequence item as a separate XML element. The name
1583-
//! of that element is taken from serialized type, and because only `enum`s provide
1584-
//! such name (their variant name), only they should be used for such fields.
1585-
//!
1586-
//! `$value` fields does not support `struct` types with fields, the serialization
1587-
//! of such types would end with an `Err(Unsupported)`. Unit structs and unit
1588-
//! type `()` serializing to nothing and can be deserialized from any content.
1580+
//! The representation of primitive types in `$value` does not differ from their
1581+
//! representation in `$text` fields. The difference is how sequences are serialized
1582+
//! and deserialized. `$value` serializes each sequence item as a separate XML element.
1583+
//! How the name of the XML element is chosen depends on the field's type. For
1584+
//! `enum`s, the variant name is used. For `struct`s, the name of the `struct`
1585+
//! is used.
1586+
//!
1587+
//! During deserialization, if the `$value` field is an enum, then the variant's
1588+
//! name is matched against. That's **not** the case with structs, however, since
1589+
//! `serde` does not expose type names of nested fields. This does mean that **any**
1590+
//! type could be deserialized into a `$value` struct-type field, so long as the
1591+
//! struct's fields have compatible types (or are captured as text by `String`
1592+
//! or similar-behaving types). This can be handy when using generic types in fields
1593+
//! where one knows in advance what to expect. If you do not know what to expect,
1594+
//! however, prefer an enum with all possible variants.
1595+
//!
1596+
//! Unit structs and unit type `()` serialize to nothing and can be deserialized
1597+
//! from any content.
15891598
//!
15901599
//! Serialization and deserialization of `$value` field performed as usual, except
15911600
//! that name for an XML element will be given by the serialized type, instead of
@@ -1646,6 +1655,51 @@
16461655
//! # );
16471656
//! ```
16481657
//!
1658+
//! The next example demonstrates how generic types can be used in conjunction
1659+
//! with `$value`-named fields to allow the reuse of wrapping structs. A common
1660+
//! example use case for this feature is SOAP messages, which can be commmonly
1661+
//! found wrapped around `<soapenv:Envelope> ... </soapenv:Envelope>`.
1662+
//!
1663+
//! ```rust
1664+
//! # use pretty_assertions::assert_eq;
1665+
//! # use quick_xml::de::from_str;
1666+
//! # use quick_xml::se::to_string;
1667+
//! # use serde::{Deserialize, Serialize};
1668+
//! #
1669+
//! #[derive(Deserialize, Serialize, PartialEq, Debug)]
1670+
//! struct Envelope<T> {
1671+
//! body: Body<T>,
1672+
//! }
1673+
//!
1674+
//! #[derive(Deserialize, Serialize, PartialEq, Debug)]
1675+
//! struct Body<T> {
1676+
//! #[serde(rename = "$value")]
1677+
//! inner: T,
1678+
//! }
1679+
//!
1680+
//! #[derive(Serialize, PartialEq, Debug)]
1681+
//! struct Example {
1682+
//! a: i32,
1683+
//! }
1684+
//!
1685+
//! assert_eq!(
1686+
//! to_string(&Envelope { body: Body { inner: Example { a: 42 } } }).unwrap(),
1687+
//! // Notice how `inner` is not present in the XML
1688+
//! "<Envelope><body><Example><a>42</a></Example></body></Envelope>",
1689+
//! );
1690+
//!
1691+
//! #[derive(Deserialize, PartialEq, Debug)]
1692+
//! struct AnotherExample {
1693+
//! a: i32,
1694+
//! }
1695+
//!
1696+
//! assert_eq!(
1697+
//! // Notice that tag the name does nothing for struct in `$value` field
1698+
//! Envelope { body: Body { inner: AnotherExample { a: 42 } } },
1699+
//! from_str("<Envelope><body><Example><a>42</a></Example></body></Envelope>").unwrap(),
1700+
//! );
1701+
//! ```
1702+
//!
16491703
//! ### Primitives and sequences of primitives
16501704
//!
16511705
//! Sequences serialized to a list of elements. Note, that types that does not

src/se/content.rs

Lines changed: 67 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ impl<'w, 'i, W: Write> Serializer for ContentSerializer<'w, 'i, W> {
179179
type SerializeTupleStruct = Seq<'w, 'i, W>;
180180
type SerializeTupleVariant = Tuple<'w, 'i, W>;
181181
type SerializeMap = Impossible<Self::Ok, Self::Error>;
182-
type SerializeStruct = Impossible<Self::Ok, Self::Error>;
182+
type SerializeStruct = Struct<'w, 'i, W>;
183183
type SerializeStructVariant = Struct<'w, 'i, W>;
184184

185185
write_primitive!(serialize_bool(bool));
@@ -352,11 +352,13 @@ impl<'w, 'i, W: Write> Serializer for ContentSerializer<'w, 'i, W> {
352352
fn serialize_struct(
353353
self,
354354
name: &'static str,
355-
_len: usize,
355+
len: usize,
356356
) -> Result<Self::SerializeStruct, Self::Error> {
357-
Err(SeError::Unsupported(
358-
format!("serialization of struct `{name}` is not supported in `$value` field").into(),
359-
))
357+
ElementSerializer {
358+
ser: self,
359+
key: XmlName::try_from(name)?,
360+
}
361+
.serialize_struct(name, len)
360362
}
361363

362364
/// Serializes variant as an element with name `variant`, producing
@@ -707,8 +709,13 @@ pub(super) mod tests {
707709
// only `enum` can provide
708710
err!(map: BTreeMap::from([("_1", 2), ("_3", 4)])
709711
=> Unsupported("serialization of map types is not supported in `$value` field"));
710-
err!(struct_: Struct { key: "answer", val: (42, 42) }
711-
=> Unsupported("serialization of struct `Struct` is not supported in `$value` field"));
712+
serialize_as!(struct_: Struct { key: "answer", val: (42, 42) }
713+
=> "<Struct>\
714+
<key>answer</key>\
715+
<val>42</val>\
716+
<val>42</val>\
717+
</Struct>");
718+
712719
serialize_as!(enum_struct: Enum::Struct { key: "answer", val: (42, 42) }
713720
=> "<Struct>\
714721
<key>answer</key>\
@@ -723,13 +730,17 @@ pub(super) mod tests {
723730

724731
err!(map: BTreeMap::from([("$text", 2), ("_3", 4)])
725732
=> Unsupported("serialization of map types is not supported in `$value` field"));
726-
err!(struct_:
733+
serialize_as!(struct_:
727734
Text {
728735
before: "answer",
729736
content: (42, 42),
730737
after: "answer",
731738
}
732-
=> Unsupported("serialization of struct `Text` is not supported in `$value` field"));
739+
=> "<Text>\
740+
<before>answer</before>\
741+
42 42\
742+
<after>answer</after>\
743+
</Text>");
733744
serialize_as!(enum_struct:
734745
SpecialEnum::Text {
735746
before: "answer",
@@ -987,13 +998,21 @@ pub(super) mod tests {
987998
after: "answer",
988999
}
9891000
=> Unsupported("serialization of map types is not supported in `$value` field"));
990-
err!(struct_:
1001+
value!(struct_:
9911002
SpecialEnum::Value {
9921003
before: "answer",
9931004
content: Struct { key: "answer", val: (42, 42) },
9941005
after: "answer",
9951006
}
996-
=> Unsupported("serialization of struct `Struct` is not supported in `$value` field"));
1007+
=> "<Value>\
1008+
<before>answer</before>\
1009+
<Struct>\
1010+
<key>answer</key>\
1011+
<val>42</val>\
1012+
<val>42</val>\
1013+
</Struct>\
1014+
<after>answer</after>\
1015+
</Value>");
9971016
value!(enum_struct:
9981017
Enum::Struct { key: "answer", val: (42, 42) }
9991018
=> "<Struct>\
@@ -1012,12 +1031,12 @@ pub(super) mod tests {
10121031
err!(map_mixed: BTreeMap::from([("@key1", 1), ("key2", 2)])
10131032
=> Unsupported("serialization of map types is not supported in `$value` field"));
10141033

1015-
err!(struct_: Attributes { key: "answer", val: (42, 42) }
1016-
=> Unsupported("serialization of struct `Attributes` is not supported in `$value` field"));
1017-
err!(struct_before: AttributesBefore { key: "answer", val: 42 }
1018-
=> Unsupported("serialization of struct `AttributesBefore` is not supported in `$value` field"));
1019-
err!(struct_after: AttributesAfter { key: "answer", val: 42 }
1020-
=> Unsupported("serialization of struct `AttributesAfter` is not supported in `$value` field"));
1034+
serialize_as!(struct_: Attributes { key: "answer", val: (42, 42) }
1035+
=> r#"<Attributes key="answer" val="42 42"/>"#);
1036+
serialize_as!(struct_before: AttributesBefore { key: "answer", val: 42 }
1037+
=> r#"<AttributesBefore key="answer"><val>42</val></AttributesBefore>"#);
1038+
serialize_as!(struct_after: AttributesAfter { key: "answer", val: 42 }
1039+
=> r#"<AttributesAfter val="42"><key>answer</key></AttributesAfter>"#);
10211040

10221041
serialize_as!(enum_: Enum::Attributes { key: "answer", val: (42, 42) }
10231042
=> r#"<Attributes key="answer" val="42 42"/>"#);
@@ -1157,8 +1176,12 @@ pub(super) mod tests {
11571176
// only `enum` can provide
11581177
err!(map: BTreeMap::from([("_1", 2), ("_3", 4)])
11591178
=> Unsupported("serialization of map types is not supported in `$value` field"));
1160-
err!(struct_: Struct { key: "answer", val: (42, 42) }
1161-
=> Unsupported("serialization of struct `Struct` is not supported in `$value` field"));
1179+
serialize_as!(struct_: Struct { key: "answer", val: (42, 42) }
1180+
=> "<Struct>\n \
1181+
<key>answer</key>\n \
1182+
<val>42</val>\n \
1183+
<val>42</val>\n\
1184+
</Struct>");
11621185
serialize_as!(enum_struct: Enum::Struct { key: "answer", val: (42, 42) }
11631186
=> "<Struct>\n \
11641187
<key>answer</key>\n \
@@ -1173,13 +1196,15 @@ pub(super) mod tests {
11731196

11741197
err!(map: BTreeMap::from([("$text", 2), ("_3", 4)])
11751198
=> Unsupported("serialization of map types is not supported in `$value` field"));
1176-
err!(struct_:
1199+
serialize_as!(struct_:
11771200
Text {
11781201
before: "answer",
11791202
content: (42, 42),
11801203
after: "answer",
11811204
}
1182-
=> Unsupported("serialization of struct `Text` is not supported in `$value` field"));
1205+
=> "<Text>\n \
1206+
<before>answer</before>42 42<after>answer</after>\n\
1207+
</Text>");
11831208
serialize_as!(enum_struct:
11841209
SpecialEnum::Text {
11851210
before: "answer",
@@ -1436,13 +1461,22 @@ pub(super) mod tests {
14361461
after: "answer",
14371462
}
14381463
=> Unsupported("serialization of map types is not supported in `$value` field"));
1439-
err!(struct_:
1464+
value!(struct_:
14401465
SpecialEnum::Value {
14411466
before: "answer",
14421467
content: Struct { key: "answer", val: (42, 42) },
14431468
after: "answer",
14441469
}
1445-
=> Unsupported("serialization of struct `Struct` is not supported in `$value` field"));
1470+
=> "\n \
1471+
<Value>\n \
1472+
<before>answer</before>\n \
1473+
<Struct>\n \
1474+
<key>answer</key>\n \
1475+
<val>42</val>\n \
1476+
<val>42</val>\n \
1477+
</Struct>\n \
1478+
<after>answer</after>\n \
1479+
</Value>\n ");
14461480
value!(enum_struct:
14471481
Enum::Struct { key: "answer", val: (42, 42) }
14481482
=> "\n \
@@ -1462,12 +1496,16 @@ pub(super) mod tests {
14621496
err!(map_mixed: BTreeMap::from([("@key1", 1), ("key2", 2)])
14631497
=> Unsupported("serialization of map types is not supported in `$value` field"));
14641498

1465-
err!(struct_: Attributes { key: "answer", val: (42, 42) }
1466-
=> Unsupported("serialization of struct `Attributes` is not supported in `$value` field"));
1467-
err!(struct_before: AttributesBefore { key: "answer", val: 42 }
1468-
=> Unsupported("serialization of struct `AttributesBefore` is not supported in `$value` field"));
1469-
err!(struct_after: AttributesAfter { key: "answer", val: 42 }
1470-
=> Unsupported("serialization of struct `AttributesAfter` is not supported in `$value` field"));
1499+
serialize_as!(struct_: Attributes { key: "answer", val: (42, 42) }
1500+
=> r#"<Attributes key="answer" val="42 42"/>"#);
1501+
serialize_as!(struct_before: AttributesBefore { key: "answer", val: 42 }
1502+
=> "<AttributesBefore key=\"answer\">\n \
1503+
<val>42</val>\n\
1504+
</AttributesBefore>");
1505+
serialize_as!(struct_after: AttributesAfter { key: "answer", val: 42 }
1506+
=> "<AttributesAfter val=\"42\">\n \
1507+
<key>answer</key>\n\
1508+
</AttributesAfter>");
14711509

14721510
serialize_as!(enum_: Enum::Attributes { key: "answer", val: (42, 42) }
14731511
=> r#"<Attributes key="answer" val="42 42"/>"#);

0 commit comments

Comments
 (0)