Skip to content

Commit bc18c57

Browse files
jschleucher-jgdMingun
authored andcommitted
serde: allow xml: attributes to roundtrip
1 parent 899d573 commit bc18c57

File tree

4 files changed

+48
-4
lines changed

4 files changed

+48
-4
lines changed

Changelog.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ types should preserve whitespace, while all other primitives have collapse behav
4040
- [#868]: Allow to have both `$text` and `$value` special fields in one struct. Previously
4141
any text will be recognized as `$value` field even when `$text` field is also presented.
4242
- [#868]: Skip text events when deserialize a sequence of items overlapped with text (including CDATA).
43+
- [#841]: Do not strip `xml` prefix from the attributes when map them to struct fields in `Deserializer`.
4344

4445
### Misc Changes
4546

@@ -50,6 +51,7 @@ types should preserve whitespace, while all other primitives have collapse behav
5051

5152
[#285]: https://github.com/tafia/quick-xml/issues/285
5253
[#766]: https://github.com/tafia/quick-xml/pull/766
54+
[#841]: https://github.com/tafia/quick-xml/issues/841
5355
[#863]: https://github.com/tafia/quick-xml/pull/863
5456
[#868]: https://github.com/tafia/quick-xml/pull/868
5557
[general entity]: https://www.w3.org/TR/xml11/#gen-entity

src/de/key.rs

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,12 @@ fn decode_name<'n>(name: QName<'n>, decoder: Decoder) -> Result<Cow<'n, str>, De
4242
/// - if it is an [`attribute`] name, put `@` in front of the identifier
4343
/// - if it is a namespace binding (`xmlns` or `xmlns:xxx`) put the decoded name
4444
/// to the identifier
45+
/// - if it is an attribute in the `xml` namespace, put the decoded name
46+
/// to the identifier
4547
/// - put the decoded [`local_name()`] of a name to the identifier
4648
///
47-
/// The final identifier looks like `[@]local_name`, or `@xmlns`, or `@xmlns:binding`
48-
/// (where `[]` means optional element).
49+
/// The final identifier looks like `[@]local_name`, or `@xmlns`, or `@xmlns:binding` or
50+
/// `xml:attribute` (where `[]` means optional element).
4951
///
5052
/// The deserializer also supports deserializing names as other primitive types:
5153
/// - numbers
@@ -89,8 +91,15 @@ impl<'i, 'd> QNameDeserializer<'i, 'd> {
8991
if name.as_namespace_binding().is_some() {
9092
decoder.decode_into(name.into_inner(), key_buf)?;
9193
} else {
92-
let local = name.local_name();
93-
decoder.decode_into(local.into_inner(), key_buf)?;
94+
// https://github.com/tafia/quick-xml/issues/841
95+
// we also want to map to the full name for `xml:xxx`, because `xml:xxx` attributes
96+
// can apper only in this literal form, as `xml` prefix cannot be redeclared or unbound
97+
let (local, prefix_opt) = name.decompose();
98+
if prefix_opt.map_or(false, |prefix| prefix.is_xml()) {
99+
decoder.decode_into(&name.into_inner(), key_buf)?;
100+
} else {
101+
decoder.decode_into(local.into_inner(), key_buf)?;
102+
}
94103
};
95104

96105
Ok(Self {

src/name.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,18 @@ impl<'a> Prefix<'a> {
257257
pub const fn into_inner(self) -> &'a [u8] {
258258
self.0
259259
}
260+
261+
/// Checks if this prefix is a special prefix `xml`.
262+
#[inline(always)]
263+
pub const fn is_xml(&self) -> bool {
264+
matches!(self.0, b"xml")
265+
}
266+
267+
/// Checks if this prefix is a special prefix `xmlns`.
268+
#[inline(always)]
269+
pub const fn is_xmlns(&self) -> bool {
270+
matches!(self.0, b"xmlns")
271+
}
260272
}
261273
impl<'a> Debug for Prefix<'a> {
262274
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {

tests/serde-issues.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -571,6 +571,27 @@ fn issue683() {
571571
);
572572
}
573573

574+
/// Regression test for https://github.com/tafia/quick-xml/issues/841.
575+
/// `xml:`-attributes should be able to roundtrip serialization - deserialization
576+
#[test]
577+
fn issue841() {
578+
#[derive(Debug, PartialEq, Deserialize, Serialize)]
579+
struct ElementWithXmlLang {
580+
#[serde(rename = "$text")]
581+
content: String,
582+
#[serde(rename = "@xml:lang")]
583+
language: String,
584+
}
585+
586+
let value = ElementWithXmlLang {
587+
content: "content".to_string(),
588+
language: "en-US".to_string(),
589+
};
590+
let sr = to_string(&value).unwrap();
591+
let ds: ElementWithXmlLang = from_str(&sr).unwrap();
592+
assert_eq!(ds, value);
593+
}
594+
574595
/// Regression test for https://github.com/tafia/quick-xml/issues/868.
575596
#[test]
576597
fn issue868() {

0 commit comments

Comments
 (0)