Skip to content

Commit 967d60d

Browse files
Mingundralley
authored andcommitted
Restore ability to deserialize attributes that represents XML namespace mappings (xmlns:xxx)
1 parent 4a09041 commit 967d60d

File tree

5 files changed

+84
-5
lines changed

5 files changed

+84
-5
lines changed

Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,11 @@ name = "serde-migrated"
212212
required-features = ["serialize"]
213213
path = "tests/serde-migrated.rs"
214214

215+
[[test]]
216+
name = "serde-issues"
217+
required-features = ["serialize"]
218+
path = "tests/serde-issues.rs"
219+
215220
[[test]]
216221
name = "async-tokio"
217222
required-features = ["async-tokio"]

Changelog.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,14 @@
1414

1515
### Bug Fixes
1616

17+
- [#537]: Restore ability to deserialize attributes that represents XML namespace
18+
mappings (`xmlns:xxx`) that was broken since [#490]
19+
1720
### Misc Changes
1821

22+
[#490]: https://github.com/tafia/quick-xml/pull/490
23+
[#537]: https://github.com/tafia/quick-xml/issues/537
24+
1925
## 0.27.1 -- 2022-12-28
2026

2127
### Bug Fixes

src/de/key.rs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,11 @@ fn decode_name<'n>(name: QName<'n>, decoder: Decoder) -> Result<Cow<'n, str>, De
3535
/// Converts a name to an identifier string using the following rules:
3636
///
3737
/// - if it is an [`attribute`] name, put `@` in front of the identifier
38+
/// - if it is a namespace binding (`xmlns` or `xmlns:xxx`) put the decoded name
39+
/// to the identifier
3840
/// - put the decoded [`local_name()`] of a name to the identifier
3941
///
40-
/// The final identifier looks like `[@]local_name`
42+
/// The final identifier looks like `[@]local_name`, or `@xmlns`, or `@xmlns:binding`
4143
/// (where `[]` means optional element).
4244
///
4345
/// The deserializer also supports deserializing names as other primitive types:
@@ -72,10 +74,16 @@ pub struct QNameDeserializer<'d> {
7274
impl<'d> QNameDeserializer<'d> {
7375
/// Creates deserializer from name of an attribute
7476
pub fn from_attr(name: QName<'d>, decoder: Decoder) -> Result<Self, DeError> {
75-
let local = decode_name(name, decoder)?;
77+
// https://github.com/tafia/quick-xml/issues/537
78+
// Namespace bindings (xmlns:xxx) map to `@xmlns:xxx` instead of `@xxx`
79+
let field = if name.as_namespace_binding().is_some() {
80+
decoder.decode(name.into_inner())?
81+
} else {
82+
decode_name(name, decoder)?
83+
};
7684

7785
Ok(Self {
78-
name: Cow::Owned(format!("@{local}")),
86+
name: Cow::Owned(format!("@{field}")),
7987
})
8088
}
8189

tests/issues.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
//! Regression tests found in various issues
1+
//! Regression tests found in various issues.
22
//!
3-
//! Name each test as `issue<GH number>`
3+
//! Name each module / test as `issue<GH number>` and keep sorted by issue number
44
55
use quick_xml::events::{BytesStart, Event};
66
use quick_xml::reader::Reader;

tests/serde-issues.rs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
//! Regression tests found in various issues with serde integration.
2+
//!
3+
//! Name each module / test as `issue<GH number>` and keep sorted by issue number
4+
5+
use quick_xml::de::from_str;
6+
use quick_xml::se::to_string;
7+
use serde::{Deserialize, Serialize};
8+
9+
/// Regression test for https://github.com/tafia/quick-xml/issues/537.
10+
///
11+
/// This test checks that special `xmlns:xxx` attributes uses full name of
12+
/// an attribute (xmlns:xxx) as a field name instead of just local name of
13+
/// an attribute (xxx)
14+
mod issue537 {
15+
use super::*;
16+
use pretty_assertions::assert_eq;
17+
18+
#[derive(Debug, PartialEq, Deserialize, Serialize)]
19+
struct Bindings<'a> {
20+
/// Default namespace binding
21+
#[serde(rename = "@xmlns")]
22+
xmlns: &'a str,
23+
24+
/// Named namespace binding
25+
#[serde(rename = "@xmlns:named")]
26+
xmlns_named: &'a str,
27+
28+
/// Usual attribute
29+
#[serde(rename = "@attribute")]
30+
attribute: &'a str,
31+
}
32+
33+
#[test]
34+
fn de() {
35+
assert_eq!(
36+
from_str::<Bindings>(
37+
r#"<Bindings xmlns="default" xmlns:named="named" attribute="attribute"/>"#
38+
)
39+
.unwrap(),
40+
Bindings {
41+
xmlns: "default",
42+
xmlns_named: "named",
43+
attribute: "attribute",
44+
}
45+
);
46+
}
47+
48+
#[test]
49+
fn se() {
50+
assert_eq!(
51+
to_string(&Bindings {
52+
xmlns: "default",
53+
xmlns_named: "named",
54+
attribute: "attribute",
55+
})
56+
.unwrap(),
57+
r#"<Bindings xmlns="default" xmlns:named="named" attribute="attribute"/>"#
58+
);
59+
}
60+
}

0 commit comments

Comments
 (0)