Skip to content

Commit 2a83743

Browse files
Mingundralley
andcommitted
ns: Add API to get namespace prefixes from QNames and fix reserved name test
Co-authored-by: Daniel Alley <[email protected]>
1 parent 30aaf6b commit 2a83743

File tree

4 files changed

+80
-35
lines changed

4 files changed

+80
-35
lines changed

Changelog.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@
1414

1515
- [#387]: Allow overlapping between elements of sequence and other elements
1616
(using new feature `overlapped-lists`)
17-
- [#393]: New module `name` with `QName`, `LocalName`, `Namespace`, and `Prefix`
18-
wrappers around byte arrays and `ResolveResult` with the result of prefix
19-
resolution to namespace
17+
- [#393]: New module `name` with `QName`, `LocalName`, `Namespace`, `Prefix`
18+
and `PrefixDeclaration` wrappers around byte arrays and `ResolveResult` with
19+
the result of namespace resolution
2020

2121
### Bug Fixes
2222

@@ -28,6 +28,7 @@
2828
another sequence. This error affects only users with the `serialize` feature enabled
2929
- [#393]: Now `event_namespace`, `attribute_namespace` and `read_event_namespaced`
3030
returns `ResolveResult::Unknown` if prefix was not registered in namespace buffer
31+
- [#393]: Fix breaking processing after encounter an attribute with a reserved name (started with "xmlns")
3132

3233
### Misc Changes
3334

src/name.rs

Lines changed: 70 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,41 @@ impl<'a> QName<'a> {
7171
}
7272
}
7373

74+
/// If that `QName` represents `"xmlns"` series of names, returns `Some`,
75+
/// otherwise `None` is returned.
76+
///
77+
/// # Examples
78+
///
79+
/// ```
80+
/// # use quick_xml::name::{QName, PrefixDeclaration};
81+
/// let qname = QName(b"xmlns");
82+
/// assert_eq!(qname.as_namespace_binding(), Some(PrefixDeclaration::Default));
83+
///
84+
/// let qname = QName(b"xmlns:prefix");
85+
/// assert_eq!(qname.as_namespace_binding(), Some(PrefixDeclaration::Named(b"prefix")));
86+
///
87+
/// // Be aware that this method does not check the validity of the prefix - it can be empty!
88+
/// let qname = QName(b"xmlns:");
89+
/// assert_eq!(qname.as_namespace_binding(), Some(PrefixDeclaration::Named(b"")));
90+
///
91+
/// let qname = QName(b"other-name");
92+
/// assert_eq!(qname.as_namespace_binding(), None);
93+
///
94+
/// // https://www.w3.org/TR/xml-names11/#xmlReserved
95+
/// let qname = QName(b"xmlns-reserved-name");
96+
/// assert_eq!(qname.as_namespace_binding(), None);
97+
/// ```
98+
pub fn as_namespace_binding(&self) -> Option<PrefixDeclaration<'a>> {
99+
if self.0.starts_with(b"xmlns") {
100+
return match self.0.get(5) {
101+
None => Some(PrefixDeclaration::Default),
102+
Some(&b':') => Some(PrefixDeclaration::Named(&self.0[6..])),
103+
_ => None,
104+
};
105+
}
106+
None
107+
}
108+
74109
/// Returns the index in the name where prefix ended
75110
#[inline(always)]
76111
fn index(&self) -> Option<usize> {
@@ -173,6 +208,19 @@ impl<'a> AsRef<[u8]> for Prefix<'a> {
173208

174209
////////////////////////////////////////////////////////////////////////////////////////////////////
175210

211+
/// A namespace prefix declaration, `xmlns` or `xmlns:<name>`, as defined in
212+
/// [XML Schema specification](https://www.w3.org/TR/xml-names/#ns-decl)
213+
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
214+
pub enum PrefixDeclaration<'a> {
215+
/// XML attribute binds a default namespace. Corresponds to `xmlns` in in `xmlns="..."`
216+
Default,
217+
/// XML attribute binds a specified prefix to a namespace. Corresponds to a
218+
/// `prefix` in `xmlns:prefix="..."`, which is stored as payload of this variant.
219+
Named(&'a [u8]),
220+
}
221+
222+
////////////////////////////////////////////////////////////////////////////////////////////////////
223+
176224
/// A [namespace name] that is declared in a `xmlns[:prefix]="namespace name"`.
177225
///
178226
/// [namespace name]: https://www.w3.org/TR/xml-names11/#dt-NSName
@@ -361,32 +409,29 @@ impl NamespaceResolver {
361409
// (default namespace) attribute.
362410
for a in start.attributes().with_checks(false) {
363411
if let Ok(Attribute { key: k, value: v }) = a {
364-
let k = k.as_ref(); //TODO: Use QName API
365-
if k.starts_with(b"xmlns") {
366-
match k.get(5) {
367-
None => {
368-
let start = buffer.len();
369-
buffer.extend_from_slice(&*v);
370-
self.bindings.push(NamespaceEntry {
371-
start,
372-
prefix_len: 0,
373-
value_len: v.len(),
374-
level,
375-
});
376-
}
377-
Some(&b':') => {
378-
let start = buffer.len();
379-
buffer.extend_from_slice(&k[6..]);
380-
buffer.extend_from_slice(&*v);
381-
self.bindings.push(NamespaceEntry {
382-
start,
383-
prefix_len: k.len() - 6,
384-
value_len: v.len(),
385-
level,
386-
});
387-
}
388-
_ => break,
412+
match k.as_namespace_binding() {
413+
Some(PrefixDeclaration::Default) => {
414+
let start = buffer.len();
415+
buffer.extend_from_slice(&*v);
416+
self.bindings.push(NamespaceEntry {
417+
start,
418+
prefix_len: 0,
419+
value_len: v.len(),
420+
level,
421+
});
422+
}
423+
Some(PrefixDeclaration::Named(prefix)) => {
424+
let start = buffer.len();
425+
buffer.extend_from_slice(prefix);
426+
buffer.extend_from_slice(&*v);
427+
self.bindings.push(NamespaceEntry {
428+
start,
429+
prefix_len: prefix.len(),
430+
value_len: v.len(),
431+
level,
432+
});
389433
}
434+
None => {}
390435
}
391436
} else {
392437
break;

tests/namespaces.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ fn attributes_empty_ns() {
157157
.attributes()
158158
.map(|ar| ar.expect("Expecting attribute parsing to succeed."))
159159
// we don't care about xmlns attributes for this test
160-
.filter(|kv| !kv.key.as_ref().starts_with(b"xmlns"))
160+
.filter(|kv| kv.key.as_namespace_binding().is_none())
161161
.map(|Attribute { key: name, value }| {
162162
let (opt_ns, local_name) = r.attribute_namespace(name, &ns_buf);
163163
(opt_ns, local_name.into_inner(), value)
@@ -198,7 +198,7 @@ fn attributes_empty_ns_expanded() {
198198
.attributes()
199199
.map(|ar| ar.expect("Expecting attribute parsing to succeed."))
200200
// we don't care about xmlns attributes for this test
201-
.filter(|kv| !kv.key.as_ref().starts_with(b"xmlns"))
201+
.filter(|kv| kv.key.as_namespace_binding().is_none())
202202
.map(|Attribute { key: name, value }| {
203203
let (opt_ns, local_name) = r.attribute_namespace(name, &ns_buf);
204204
(opt_ns, local_name.into_inner(), value)
@@ -259,7 +259,7 @@ fn default_ns_shadowing_empty() {
259259
.attributes()
260260
.map(|ar| ar.expect("Expecting attribute parsing to succeed."))
261261
// we don't care about xmlns attributes for this test
262-
.filter(|kv| !kv.key.as_ref().starts_with(b"xmlns"))
262+
.filter(|kv| kv.key.as_namespace_binding().is_none())
263263
.map(|Attribute { key: name, value }| {
264264
let (opt_ns, local_name) = r.attribute_namespace(name, &ns_buf);
265265
(opt_ns, local_name.into_inner(), value)
@@ -318,7 +318,7 @@ fn default_ns_shadowing_expanded() {
318318
.attributes()
319319
.map(|ar| ar.expect("Expecting attribute parsing to succeed."))
320320
// we don't care about xmlns attributes for this test
321-
.filter(|kv| !kv.key.as_ref().starts_with(b"xmlns"))
321+
.filter(|kv| kv.key.as_namespace_binding().is_none())
322322
.map(|Attribute { key: name, value }| {
323323
let (opt_ns, local_name) = r.attribute_namespace(name, &ns_buf);
324324
(opt_ns, local_name.into_inner(), value)

tests/xmlrs_reader_tests.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -444,11 +444,10 @@ fn make_attrs(e: &BytesStart) -> ::std::result::Result<String, String> {
444444
for a in e.attributes() {
445445
match a {
446446
Ok(a) => {
447-
let key = a.key.as_ref();
448-
if !key.starts_with(b"xmlns") {
447+
if a.key.as_namespace_binding().is_none() {
449448
atts.push(format!(
450449
"{}=\"{}\"",
451-
from_utf8(key).unwrap(),
450+
from_utf8(a.key.as_ref()).unwrap(),
452451
from_utf8(&*a.unescaped_value().unwrap()).unwrap()
453452
));
454453
}

0 commit comments

Comments
 (0)