Repository: tafia/quick-xml
Pull request #393 (2022-06-05 16:51:41)

Introduce typified wrappers for names and fix couple of bugs
Fixed bugs:
- not declared prefix in the tags resulted in a namespace resolution, as if the tag were not bound to any namespace:
  ```xml
  <unknown:element/>
  ```
  was considered as
  ```xml
  <element/>
  ```
- elements/attributes with [reserved](https://www.w3.org/TR/xml-names11/#xmlReserved) names (started with `"xml"i`) erroneously was treated as error, although XML spec explicitly requires that fatal error MUST NOT be raised. Because quick-xml does not check names validity, now such names does not produce any error.

To fix this kind of errors and avoid messing up different names, a typified wrappers for names was introduced.

This PR is the first step to properly support namespaces in quick-xml.


Patch:
diff --git a/Changelog.md b/Changelog.md
index f55c3f5..f9d54bc 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -14,6 +14,9 @@
 
 - [#387]: Allow overlapping between elements of sequence and other elements
   (using new feature `overlapped-lists`)
+- [#393]: New module `name` with `QName`, `LocalName`, `Namespace`, `Prefix`
+  and `PrefixDeclaration` wrappers around byte arrays and `ResolveResult` with
+  the result of namespace resolution
 
 ### Bug Fixes
 
@@ -23,6 +26,9 @@
 - [#387]: Internal deserializer state can be broken when deserializing a map with
   a sequence field (such as `Vec<T>`), where elements of this sequence contains
   another sequence. This error affects only users with the `serialize` feature enabled
+- [#393]: Now `event_namespace`, `attribute_namespace` and `read_event_namespaced`
+  returns `ResolveResult::Unknown` if prefix was not registered in namespace buffer
+- [#393]: Fix breaking processing after encounter an attribute with a reserved name (started with "xmlns")
 
 ### Misc Changes
 
@@ -42,15 +48,25 @@
 
 - [#391]: Added code coverage
 
+- [#393]: `event_namespace` and `attribute_namespace` now accept `QName`
+  and returns `ResolveResult` and `LocalName`, `read_event_namespaced` now
+  returns `ResolveResult` instead of `Option<[u8]>`
+- [#393]: Types of `Attribute::key` and `Attr::key()` changed to `QName`
+- [#393]: Now `BytesStart::name()` and `BytesEnd::name()` returns `QName`, and
+  `BytesStart::local_name()` and `BytesEnd::local_name()` returns `LocalName`
+
 ### New Tests
 
 - [#9]: Added tests for incorrect nested tags in input
 - [#387]: Added a bunch of tests for sequences deserialization
+- [#393]: Added more tests for namespace resolver
+- [#393]: Added tests for reserved names (started with "xml"i) -- see <https://www.w3.org/TR/xml-names11/#xmlReserved>
 
 [#8]: https://github.com/Mingun/fast-xml/pull/8
 [#9]: https://github.com/Mingun/fast-xml/pull/9
 [#387]: https://github.com/tafia/quick-xml/pull/387
 [#391]: https://github.com/tafia/quick-xml/pull/391
+[#393]: https://github.com/tafia/quick-xml/pull/393
 
 ## 0.23.0 -- 2022-05-08
 
diff --git a/examples/custom_entities.rs b/examples/custom_entities.rs
index ddd0b57..1212f34 100644
--- a/examples/custom_entities.rs
+++ b/examples/custom_entities.rs
@@ -37,7 +37,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
                     custom_entities.insert(cap[1].to_vec(), cap[2].to_vec());
                 }
             }
-            Ok(Event::Start(ref e)) => match e.name() {
+            Ok(Event::Start(ref e)) => match e.name().as_ref() {
                 b"test" => println!(
                     "attributes values: {:?}",
                     e.attributes()
diff --git a/examples/issue68.rs b/examples/issue68.rs
index f5c62e9..ccb0e98 100644
--- a/examples/issue68.rs
+++ b/examples/issue68.rs
@@ -1,7 +1,9 @@
 #![allow(unused)]
 
 use quick_xml::events::Event;
+use quick_xml::name::Namespace;
 use quick_xml::Reader;
+use std::convert::TryFrom;
 use std::io::Read;
 
 struct Resource {
@@ -81,9 +83,11 @@ fn parse_report(xml_data: &str) -> Vec<Resource> {
 
     loop {
         match reader.read_namespaced_event(&mut buf, &mut ns_buffer) {
-            Ok((namespace_value, Event::Start(e))) => {
-                let namespace_value = namespace_value.unwrap_or_default();
-                match (depth, state, namespace_value, e.local_name()) {
+            Ok((ns, Event::Start(e))) => {
+                let ns = Option::<Namespace>::try_from(ns)
+                    .unwrap_or_default() // Treat unknown prefixes as not bound to any namespace
+                    .unwrap_or(Namespace(b""));
+                match (depth, state, ns.as_ref(), e.local_name().as_ref()) {
                     (0, State::Root, b"DAV:", b"multistatus") => state = State::MultiStatus,
                     (1, State::MultiStatus, b"DAV:", b"response") => {
                         state = State::Response;
@@ -96,10 +100,11 @@ fn parse_report(xml_data: &str) -> Vec<Resource> {
                 }
                 depth += 1;
             }
-            Ok((namespace_value, Event::End(e))) => {
-                let namespace_value = namespace_value.unwrap_or_default();
-                let local_name = e.local_name();
-                match (depth, state, &*namespace_value, local_name) {
+            Ok((ns, Event::End(e))) => {
+                let ns = Option::<Namespace>::try_from(ns)
+                    .unwrap_or_default() // Treat unknown prefixes as not bound to any namespace
+                    .unwrap_or(Namespace(b""));
+                match (depth, state, ns.as_ref(), e.local_name().as_ref()) {
                     (1, State::MultiStatus, b"DAV:", b"multistatus") => state = State::Root,
                     (2, State::MultiStatus, b"DAV:", b"multistatus") => state = State::MultiStatus,
                     _ => {}
diff --git a/examples/nested_readers.rs b/examples/nested_readers.rs
index 95ac5f1..2b348da 100644
--- a/examples/nested_readers.rs
+++ b/examples/nested_readers.rs
@@ -21,7 +21,7 @@ fn main() -> Result<(), quick_xml::Error> {
     let mut found_tables = Vec::new();
     loop {
         match reader.read_event(&mut buf)? {
-            Event::Start(element) => match element.name() {
+            Event::Start(element) => match element.name().as_ref() {
                 b"w:tbl" => {
                     count += 1;
                     let mut stats = TableStat {
@@ -34,19 +34,21 @@ fn main() -> Result<(), quick_xml::Error> {
                     loop {
                         skip_buf.clear();
                         match reader.read_event(&mut skip_buf)? {
-                            Event::Start(element) => match element.name() {
+                            Event::Start(element) => match element.name().as_ref() {
                                 b"w:tr" => {
                                     stats.rows.push(vec![]);
                                     row_index = stats.rows.len() - 1;
                                 }
                                 b"w:tc" => {
-                                    stats.rows[row_index]
-                                        .push(String::from_utf8(element.name().to_vec()).unwrap());
+                                    stats.rows[row_index].push(
+                                        String::from_utf8(element.name().as_ref().to_vec())
+                                            .unwrap(),
+                                    );
                                 }
                                 _ => {}
                             },
                             Event::End(element) => {
-                                if element.name() == b"w:tbl" {
+                                if element.name().as_ref() == b"w:tbl" {
                                     found_tables.push(stats);
                                     break;
                                 }
diff --git a/examples/read_texts.rs b/examples/read_texts.rs
index 11535fb..dc6f9bd 100644
--- a/examples/read_texts.rs
+++ b/examples/read_texts.rs
@@ -13,7 +13,7 @@ fn main() {
 
     loop {
         match reader.read_event(&mut buf) {
-            Ok(Event::Start(ref e)) if e.name() == b"tag2" => {
+            Ok(Event::Start(ref e)) if e.name().as_ref() == b"tag2" => {
                 txt.push(
                     reader
                         .read_text(b"tag2", &mut Vec::new())
diff --git a/src/de/map.rs b/src/de/map.rs
index dfe2467..2ee697d 100644
--- a/src/de/map.rs
+++ b/src/de/map.rs
@@ -275,7 +275,7 @@ where
                     let key = if let Some(p) = self
                         .unflatten_fields
                         .iter()
-                        .position(|f| e.name() == &f[UNFLATTEN_PREFIX.len()..])
+                        .position(|f| e.name().as_ref() == &f[UNFLATTEN_PREFIX.len()..])
                     {
                         // Used to deserialize elements, like:
                         // <root>
@@ -290,7 +290,7 @@ where
                         // }
                         seed.deserialize(self.unflatten_fields.remove(p).into_deserializer())
                     } else {
-                        let name = Cow::Borrowed(e.local_name());
+                        let name = Cow::Borrowed(e.local_name().into_inner());
                         seed.deserialize(EscapedDeserializer::new(name, decoder, false))
                     };
                     key.map(Some)
@@ -606,7 +606,7 @@ where
                 // Stop iteration after reaching a closing tag
                 DeEvent::End(e) if e.name() == self.map.start.name() => Ok(None),
                 // This is a unmatched closing tag, so the XML is invalid
-                DeEvent::End(e) => Err(DeError::UnexpectedEnd(e.name().to_owned())),
+                DeEvent::End(e) => Err(DeError::UnexpectedEnd(e.name().as_ref().to_owned())),
                 // We cannot get `Eof` legally, because we always inside of the
                 // opened tag `self.map.start`
                 DeEvent::Eof => Err(DeError::UnexpectedEof),
diff --git a/src/de/mod.rs b/src/de/mod.rs
index 71ba50c..3529f32 100644
--- a/src/de/mod.rs
+++ b/src/de/mod.rs
@@ -221,6 +221,7 @@ pub use crate::errors::serialize::DeError;
 use crate::{
     errors::Error,
     events::{BytesCData, BytesEnd, BytesStart, BytesText, Event},
+    name::QName,
     reader::Decoder,
     Reader,
 };
@@ -514,16 +515,16 @@ where
         match self.write.back() {
             // Skip all subtree, if we skip a start event
             Some(DeEvent::Start(e)) => {
-                let end = e.name().to_owned();
+                let end = e.name().as_ref().to_owned();
                 let mut depth = 0;
                 loop {
                     let event = self.next()?;
                     match event {
-                        DeEvent::Start(ref e) if e.name() == end => {
+                        DeEvent::Start(ref e) if e.name().as_ref() == end => {
                             self.skip_event(event)?;
                             depth += 1;
                         }
-                        DeEvent::End(ref e) if e.name() == end => {
+                        DeEvent::End(ref e) if e.name().as_ref() == end => {
                             self.skip_event(event)?;
                             if depth == 0 {
                                 return Ok(());
@@ -571,7 +572,9 @@ where
             let e = self.next()?;
             match e {
                 DeEvent::Start(e) => return Ok(Some(e)),
-                DeEvent::End(e) => return Err(DeError::UnexpectedEnd(e.name().to_owned())),
+                DeEvent::End(e) => {
+                    return Err(DeError::UnexpectedEnd(e.name().as_ref().to_owned()))
+                }
                 DeEvent::Eof => return Ok(None),
                 _ => (), // ignore texts
             }
@@ -631,20 +634,24 @@ where
                     DeEvent::Text(t) if unescape => t.unescape()?,
                     DeEvent::Text(t) => BytesCData::new(t.into_inner()),
                     DeEvent::CData(t) => t,
-                    DeEvent::Start(s) => return Err(DeError::UnexpectedStart(s.name().to_owned())),
+                    DeEvent::Start(s) => {
+                        return Err(DeError::UnexpectedStart(s.name().as_ref().to_owned()))
+                    }
                     // We can get End event in case of `<tag></tag>` or `<tag/>` input
                     // Return empty text in that case
                     DeEvent::End(end) if end.name() == e.name() => {
                         return Ok(BytesCData::new(&[] as &[u8]));
                     }
-                    DeEvent::End(end) => return Err(DeError::UnexpectedEnd(end.name().to_owned())),
+                    DeEvent::End(end) => {
+                        return Err(DeError::UnexpectedEnd(end.name().as_ref().to_owned()))
+                    }
                     DeEvent::Eof => return Err(DeError::UnexpectedEof),
                 };
                 self.read_to_end(e.name())?;
                 Ok(t)
             }
-            DeEvent::Start(e) => Err(DeError::UnexpectedStart(e.name().to_owned())),
-            DeEvent::End(e) => Err(DeError::UnexpectedEnd(e.name().to_owned())),
+            DeEvent::Start(e) => Err(DeError::UnexpectedStart(e.name().as_ref().to_owned())),
+            DeEvent::End(e) => Err(DeError::UnexpectedEnd(e.name().as_ref().to_owned())),
             DeEvent::Eof => Err(DeError::UnexpectedEof),
         }
     }
@@ -658,7 +665,7 @@ where
     /// Drops all events until event with [name](BytesEnd::name()) `name` won't be
     /// dropped. This method should be called after [`Self::next()`]
     #[cfg(feature = "overlapped-lists")]
-    fn read_to_end(&mut self, name: &[u8]) -> Result<(), DeError> {
+    fn read_to_end(&mut self, name: QName) -> Result<(), DeError> {
         let mut depth = 0;
         loop {
             match self.read.pop_front() {
@@ -682,7 +689,7 @@ where
         }
     }
     #[cfg(not(feature = "overlapped-lists"))]
-    fn read_to_end(&mut self, name: &[u8]) -> Result<(), DeError> {
+    fn read_to_end(&mut self, name: QName) -> Result<(), DeError> {
         // First one might be in self.peek
         match self.next()? {
             DeEvent::Start(e) => self.reader.read_to_end(e.name())?,
@@ -751,10 +758,10 @@ where
     {
         // Try to go to the next `<tag ...>...</tag>` or `<tag .../>`
         if let Some(e) = self.next_start()? {
-            let name = e.name().to_vec();
+            let name = e.name().as_ref().to_vec();
             let map = map::MapAccess::new(self, e, fields)?;
             let value = visitor.visit_map(map)?;
-            self.read_to_end(&name)?;
+            self.read_to_end(QName(&name))?;
             Ok(value)
         } else {
             Err(DeError::ExpectedStart)
@@ -789,7 +796,7 @@ where
                 visitor.visit_unit()
             }
             DeEvent::Text(_) | DeEvent::CData(_) => visitor.visit_unit(),
-            DeEvent::End(e) => Err(DeError::UnexpectedEnd(e.name().to_owned())),
+            DeEvent::End(e) => Err(DeError::UnexpectedEnd(e.name().as_ref().to_owned())),
             DeEvent::Eof => Err(DeError::UnexpectedEof),
         }
     }
@@ -893,7 +900,7 @@ where
     {
         match self.next()? {
             DeEvent::Start(e) => self.read_to_end(e.name())?,
-            DeEvent::End(e) => return Err(DeError::UnexpectedEnd(e.name().to_owned())),
+            DeEvent::End(e) => return Err(DeError::UnexpectedEnd(e.name().as_ref().to_owned())),
             DeEvent::Eof => return Err(DeError::UnexpectedEof),
             _ => (),
         }
@@ -925,7 +932,7 @@ pub trait XmlRead<'i> {
 
     /// Skips until end element is found. Unlike `next()` it will not allocate
     /// when it cannot satisfy the lifetime.
-    fn read_to_end(&mut self, name: &[u8]) -> Result<(), DeError>;
+    fn read_to_end(&mut self, name: QName) -> Result<(), DeError>;
 
     /// A copy of the reader's decoder used to decode strings.
     fn decoder(&self) -> Decoder;
@@ -960,7 +967,7 @@ impl<'i, R: BufRead> XmlRead<'i> for IoReader<R> {
         event
     }
 
-    fn read_to_end(&mut self, name: &[u8]) -> Result<(), DeError> {
+    fn read_to_end(&mut self, name: QName) -> Result<(), DeError> {
         match self.reader.read_to_end(name, &mut self.buf) {
             Err(Error::UnexpectedEof(_)) => Err(DeError::UnexpectedEof),
             other => Ok(other?),
@@ -996,7 +1003,7 @@ impl<'de> XmlRead<'de> for SliceReader<'de> {
         }
     }
 
-    fn read_to_end(&mut self, name: &[u8]) -> Result<(), DeError> {
+    fn read_to_end(&mut self, name: QName) -> Result<(), DeError> {
         match self.reader.read_to_end_unbuffered(name) {
             Err(Error::UnexpectedEof(_)) => Err(DeError::UnexpectedEof),
             other => Ok(other?),
@@ -1212,7 +1219,7 @@ mod tests {
                 de.next().unwrap(),
                 Start(BytesStart::borrowed_name(b"target"))
             );
-            de.read_to_end(b"target").unwrap();
+            de.read_to_end(QName(b"target")).unwrap();
             assert_eq!(de.read, vec![]);
             assert_eq!(
                 de.write,
@@ -1252,7 +1259,7 @@ mod tests {
                 de.next().unwrap(),
                 Start(BytesStart::borrowed_name(b"skip"))
             );
-            de.read_to_end(b"skip").unwrap();
+            de.read_to_end(QName(b"skip")).unwrap();
 
             assert_eq!(de.next().unwrap(), End(BytesEnd::borrowed(b"root")));
         }
@@ -1313,7 +1320,7 @@ mod tests {
             de.next().unwrap(),
             Start(BytesStart::borrowed(br#"tag a="1""#, 3))
         );
-        assert_eq!(de.read_to_end(b"tag").unwrap(), ());
+        assert_eq!(de.read_to_end(QName(b"tag")).unwrap(), ());
 
         assert_eq!(
             de.next().unwrap(),
@@ -1329,7 +1336,7 @@ mod tests {
             de.next().unwrap(),
             Start(BytesStart::borrowed(b"self-closed", 11))
         );
-        assert_eq!(de.read_to_end(b"self-closed").unwrap(), ());
+        assert_eq!(de.read_to_end(QName(b"self-closed")).unwrap(), ());
 
         assert_eq!(de.next().unwrap(), End(BytesEnd::borrowed(b"root")));
         assert_eq!(de.next().unwrap(), Eof);
@@ -1432,7 +1439,7 @@ mod tests {
             reader.next().unwrap(),
             DeEvent::Start(BytesStart::borrowed(b"item ", 4))
         );
-        reader.read_to_end(b"item").unwrap();
+        reader.read_to_end(QName(b"item")).unwrap();
         assert_eq!(reader.next().unwrap(), DeEvent::Eof);
     }
 
diff --git a/src/de/seq.rs b/src/de/seq.rs
index a1a5b9d..958f172 100644
--- a/src/de/seq.rs
+++ b/src/de/seq.rs
@@ -15,10 +15,10 @@ pub fn not_in(
     decoder: Decoder,
 ) -> Result<bool, DeError> {
     #[cfg(not(feature = "encoding"))]
-    let tag = Cow::Borrowed(decoder.decode(start.name())?);
+    let tag = Cow::Borrowed(decoder.decode(start.name().into_inner())?);
 
     #[cfg(feature = "encoding")]
-    let tag = decoder.decode(start.name());
+    let tag = decoder.decode(start.name().into_inner());
 
     Ok(fields.iter().all(|&field| field != tag.as_ref()))
 }
diff --git a/src/de/var.rs b/src/de/var.rs
index 2dbdd60..643825f 100644
--- a/src/de/var.rs
+++ b/src/de/var.rs
@@ -38,7 +38,9 @@ where
             DeEvent::Text(t) => EscapedDeserializer::new(Cow::Borrowed(t), decoder, true),
             // Escape sequences does not processed inside CDATA section
             DeEvent::CData(t) => EscapedDeserializer::new(Cow::Borrowed(t), decoder, false),
-            DeEvent::Start(e) => EscapedDeserializer::new(Cow::Borrowed(e.name()), decoder, false),
+            DeEvent::Start(e) => {
+                EscapedDeserializer::new(Cow::Borrowed(e.name().into_inner()), decoder, false)
+            }
             _ => {
                 return Err(DeError::Unsupported(
                     "Invalid event for Enum, expecting `Text` or `Start`",
diff --git a/src/errors.rs b/src/errors.rs
index 54a8720..79eb789 100644
--- a/src/errors.rs
+++ b/src/errors.rs
@@ -2,6 +2,7 @@
 
 use crate::escape::EscapeError;
 use crate::events::attributes::AttrError;
+use crate::utils::write_byte_string;
 use std::str::Utf8Error;
 
 /// The error type used by this crate.
@@ -32,6 +33,8 @@ pub enum Error {
     InvalidAttr(AttrError),
     /// Escape error
     EscapeError(EscapeError),
+    /// Specified namespace prefix is unknown, cannot resolve namespace for it
+    UnknownPrefix(Vec<u8>),
 }
 
 impl From<::std::io::Error> for Error {
@@ -93,6 +96,11 @@ impl std::fmt::Display for Error {
             ),
             Error::InvalidAttr(e) => write!(f, "error while parsing attribute: {}", e),
             Error::EscapeError(e) => write!(f, "{}", e),
+            Error::UnknownPrefix(prefix) => {
+                f.write_str("Unknown namespace prefix '")?;
+                write_byte_string(f, &prefix)?;
+                f.write_str("'")
+            }
         }
     }
 }
diff --git a/src/events/attributes.rs b/src/events/attributes.rs
index b9780a8..1d8e258 100644
--- a/src/events/attributes.rs
+++ b/src/events/attributes.rs
@@ -4,6 +4,7 @@
 
 use crate::errors::{Error, Result as XmlResult};
 use crate::escape::{do_unescape, escape};
+use crate::name::QName;
 use crate::reader::{is_whitespace, Reader};
 use crate::utils::{write_byte_string, write_cow_string, Bytes};
 use std::fmt::{self, Debug, Display, Formatter};
@@ -25,7 +26,7 @@ pub struct Attribute<'a> {
     /// If [`Attributes::with_checks`] is turned off, the key might not be unique.
     ///
     /// [`Attributes::with_checks`]: struct.Attributes.html#method.with_checks
-    pub key: &'a [u8],
+    pub key: QName<'a>,
     /// The raw value of the attribute.
     pub value: Cow<'a, [u8]>,
 }
@@ -229,7 +230,7 @@ impl<'a> Attribute<'a> {
 impl<'a> Debug for Attribute<'a> {
     fn fmt(&self, f: &mut Formatter) -> fmt::Result {
         write!(f, "Attribute {{ key: ")?;
-        write_byte_string(f, self.key)?;
+        write_byte_string(f, self.key.as_ref())?;
         write!(f, ", value: ")?;
         write_cow_string(f, &self.value)?;
         write!(f, " }}")
@@ -251,7 +252,7 @@ impl<'a> From<(&'a [u8], &'a [u8])> for Attribute<'a> {
     /// ```
     fn from(val: (&'a [u8], &'a [u8])) -> Attribute<'a> {
         Attribute {
-            key: val.0,
+            key: QName(val.0),
             value: Cow::from(val.1),
         }
     }
@@ -272,7 +273,7 @@ impl<'a> From<(&'a str, &'a str)> for Attribute<'a> {
     /// ```
     fn from(val: (&'a str, &'a str)) -> Attribute<'a> {
         Attribute {
-            key: val.0.as_bytes(),
+            key: QName(val.0.as_bytes()),
             value: escape(val.1.as_bytes()),
         }
     }
@@ -525,13 +526,13 @@ impl<T> Attr<T> {
 impl<'a> Attr<&'a [u8]> {
     /// Returns the key value
     #[inline]
-    pub fn key(&self) -> &'a [u8] {
-        match self {
+    pub fn key(&self) -> QName<'a> {
+        QName(match self {
             Attr::DoubleQ(key, _) => key,
             Attr::SingleQ(key, _) => key,
             Attr::Empty(key) => key,
             Attr::Unquoted(key, _) => key,
-        }
+        })
     }
     /// Returns the attribute value. For [`Self::Empty`] variant an empty slice
     /// is returned according to the [HTML specification].
@@ -917,7 +918,7 @@ mod xml {
             assert_eq!(
                 iter.next(),
                 Some(Ok(Attribute {
-                    key: b"key",
+                    key: QName(b"key"),
                     value: Cow::Borrowed(b"value"),
                 }))
             );
@@ -933,7 +934,7 @@ mod xml {
             assert_eq!(
                 iter.next(),
                 Some(Ok(Attribute {
-                    key: b"key",
+                    key: QName(b"key"),
                     value: Cow::Borrowed(b"value"),
                 }))
             );
@@ -973,7 +974,7 @@ mod xml {
             assert_eq!(
                 iter.next(),
                 Some(Ok(Attribute {
-                    key: b"'key'",
+                    key: QName(b"'key'"),
                     value: Cow::Borrowed(b"value"),
                 }))
             );
@@ -991,7 +992,7 @@ mod xml {
             assert_eq!(
                 iter.next(),
                 Some(Ok(Attribute {
-                    key: b"key&jey",
+                    key: QName(b"key&jey"),
                     value: Cow::Borrowed(b"value"),
                 }))
             );
@@ -1024,14 +1025,14 @@ mod xml {
             assert_eq!(
                 iter.next(),
                 Some(Ok(Attribute {
-                    key: b"key",
+                    key: QName(b"key"),
                     value: Cow::Borrowed(b"value"),
                 }))
             );
             assert_eq!(
                 iter.next(),
                 Some(Ok(Attribute {
-                    key: b"regular",
+                    key: QName(b"regular"),
                     value: Cow::Borrowed(b"attribute"),
                 }))
             );
@@ -1047,14 +1048,14 @@ mod xml {
             assert_eq!(
                 iter.next(),
                 Some(Ok(Attribute {
-                    key: b"key",
+                    key: QName(b"key"),
                     value: Cow::Borrowed(b"value"),
                 }))
             );
             assert_eq!(
                 iter.next(),
                 Some(Ok(Attribute {
-                    key: b"regular",
+                    key: QName(b"regular"),
                     value: Cow::Borrowed(b"attribute"),
                 }))
             );
@@ -1073,7 +1074,7 @@ mod xml {
             assert_eq!(
                 iter.next(),
                 Some(Ok(Attribute {
-                    key: b"regular",
+                    key: QName(b"regular"),
                     value: Cow::Borrowed(b"attribute"),
                 }))
             );
@@ -1092,7 +1093,7 @@ mod xml {
             assert_eq!(
                 iter.next(),
                 Some(Ok(Attribute {
-                    key: b"regular",
+                    key: QName(b"regular"),
                     value: Cow::Borrowed(b"attribute"),
                 }))
             );
@@ -1110,14 +1111,14 @@ mod xml {
             assert_eq!(
                 iter.next(),
                 Some(Ok(Attribute {
-                    key: b"'key'",
+                    key: QName(b"'key'"),
                     value: Cow::Borrowed(b"value"),
                 }))
             );
             assert_eq!(
                 iter.next(),
                 Some(Ok(Attribute {
-                    key: b"regular",
+                    key: QName(b"regular"),
                     value: Cow::Borrowed(b"attribute"),
                 }))
             );
@@ -1135,14 +1136,14 @@ mod xml {
             assert_eq!(
                 iter.next(),
                 Some(Ok(Attribute {
-                    key: b"key&jey",
+                    key: QName(b"key&jey"),
                     value: Cow::Borrowed(b"value"),
                 }))
             );
             assert_eq!(
                 iter.next(),
                 Some(Ok(Attribute {
-                    key: b"regular",
+                    key: QName(b"regular"),
                     value: Cow::Borrowed(b"attribute"),
                 }))
             );
@@ -1219,7 +1220,7 @@ mod xml {
             assert_eq!(
                 iter.next(),
                 Some(Ok(Attribute {
-                    key: b"key",
+                    key: QName(b"key"),
                     value: Cow::Borrowed(b"value"),
                 }))
             );
@@ -1235,7 +1236,7 @@ mod xml {
             assert_eq!(
                 iter.next(),
                 Some(Ok(Attribute {
-                    key: b"key",
+                    key: QName(b"key"),
                     value: Cow::Borrowed(b"value"),
                 }))
             );
@@ -1275,7 +1276,7 @@ mod xml {
             assert_eq!(
                 iter.next(),
                 Some(Ok(Attribute {
-                    key: b"'key'",
+                    key: QName(b"'key'"),
                     value: Cow::Borrowed(b"value"),
                 }))
             );
@@ -1293,7 +1294,7 @@ mod xml {
             assert_eq!(
                 iter.next(),
                 Some(Ok(Attribute {
-                    key: b"key&jey",
+                    key: QName(b"key&jey"),
                     value: Cow::Borrowed(b"value"),
                 }))
             );
@@ -1331,7 +1332,7 @@ mod xml {
                 assert_eq!(
                     iter.next(),
                     Some(Ok(Attribute {
-                        key: b"key",
+                        key: QName(b"key"),
                         value: Cow::Borrowed(b"value"),
                     }))
                 );
@@ -1339,7 +1340,7 @@ mod xml {
                 assert_eq!(
                     iter.next(),
                     Some(Ok(Attribute {
-                        key: b"another",
+                        key: QName(b"another"),
                         value: Cow::Borrowed(b""),
                     }))
                 );
@@ -1356,7 +1357,7 @@ mod xml {
                 assert_eq!(
                     iter.next(),
                     Some(Ok(Attribute {
-                        key: b"key",
+                        key: QName(b"key"),
                         value: Cow::Borrowed(b"value"),
                     }))
                 );
@@ -1364,7 +1365,7 @@ mod xml {
                 assert_eq!(
                     iter.next(),
                     Some(Ok(Attribute {
-                        key: b"another",
+                        key: QName(b"another"),
                         value: Cow::Borrowed(b""),
                     }))
                 );
@@ -1381,7 +1382,7 @@ mod xml {
                 assert_eq!(
                     iter.next(),
                     Some(Ok(Attribute {
-                        key: b"key",
+                        key: QName(b"key"),
                         value: Cow::Borrowed(b"value"),
                     }))
                 );
@@ -1389,7 +1390,7 @@ mod xml {
                 assert_eq!(
                     iter.next(),
                     Some(Ok(Attribute {
-                        key: b"another",
+                        key: QName(b"another"),
                         value: Cow::Borrowed(b""),
                     }))
                 );
@@ -1406,7 +1407,7 @@ mod xml {
                 assert_eq!(
                     iter.next(),
                     Some(Ok(Attribute {
-                        key: b"key",
+                        key: QName(b"key"),
                         value: Cow::Borrowed(b"value"),
                     }))
                 );
@@ -1414,7 +1415,7 @@ mod xml {
                 assert_eq!(
                     iter.next(),
                     Some(Ok(Attribute {
-                        key: b"another",
+                        key: QName(b"another"),
                         value: Cow::Borrowed(b""),
                     }))
                 );
@@ -1437,21 +1438,21 @@ mod xml {
                 assert_eq!(
                     iter.next(),
                     Some(Ok(Attribute {
-                        key: b"key",
+                        key: QName(b"key"),
                         value: Cow::Borrowed(b"value"),
                     }))
                 );
                 assert_eq!(
                     iter.next(),
                     Some(Ok(Attribute {
-                        key: b"key",
+                        key: QName(b"key"),
                         value: Cow::Borrowed(b"dup"),
                     }))
                 );
                 assert_eq!(
                     iter.next(),
                     Some(Ok(Attribute {
-                        key: b"another",
+                        key: QName(b"another"),
                         value: Cow::Borrowed(b""),
                     }))
                 );
@@ -1468,21 +1469,21 @@ mod xml {
                 assert_eq!(
                     iter.next(),
                     Some(Ok(Attribute {
-                        key: b"key",
+                        key: QName(b"key"),
                         value: Cow::Borrowed(b"value"),
                     }))
                 );
                 assert_eq!(
                     iter.next(),
                     Some(Ok(Attribute {
-                        key: b"key",
+                        key: QName(b"key"),
                         value: Cow::Borrowed(b"dup"),
                     }))
                 );
                 assert_eq!(
                     iter.next(),
                     Some(Ok(Attribute {
-                        key: b"another",
+                        key: QName(b"another"),
                         value: Cow::Borrowed(b""),
                     }))
                 );
@@ -1500,7 +1501,7 @@ mod xml {
                 assert_eq!(
                     iter.next(),
                     Some(Ok(Attribute {
-                        key: b"key",
+                        key: QName(b"key"),
                         value: Cow::Borrowed(b"value"),
                     }))
                 );
@@ -1508,7 +1509,7 @@ mod xml {
                 assert_eq!(
                     iter.next(),
                     Some(Ok(Attribute {
-                        key: b"another",
+                        key: QName(b"another"),
                         value: Cow::Borrowed(b""),
                     }))
                 );
@@ -1526,7 +1527,7 @@ mod xml {
                 assert_eq!(
                     iter.next(),
                     Some(Ok(Attribute {
-                        key: b"key",
+                        key: QName(b"key"),
                         value: Cow::Borrowed(b"value"),
                     }))
                 );
@@ -1534,7 +1535,7 @@ mod xml {
                 assert_eq!(
                     iter.next(),
                     Some(Ok(Attribute {
-                        key: b"another",
+                        key: QName(b"another"),
                         value: Cow::Borrowed(b""),
                     }))
                 );
@@ -1551,28 +1552,28 @@ mod xml {
         assert_eq!(
             iter.next(),
             Some(Ok(Attribute {
-                key: b"a",
+                key: QName(b"a"),
                 value: Cow::Borrowed(b"a"),
             }))
         );
         assert_eq!(
             iter.next(),
             Some(Ok(Attribute {
-                key: b"b",
+                key: QName(b"b"),
                 value: Cow::Borrowed(b"b"),
             }))
         );
         assert_eq!(
             iter.next(),
             Some(Ok(Attribute {
-                key: b"c",
+                key: QName(b"c"),
                 value: Cow::Borrowed(br#"cc"cc"#),
             }))
         );
         assert_eq!(
             iter.next(),
             Some(Ok(Attribute {
-                key: b"d",
+                key: QName(b"d"),
                 value: Cow::Borrowed(b"dd'dd"),
             }))
         );
@@ -1604,7 +1605,7 @@ mod html {
             assert_eq!(
                 iter.next(),
                 Some(Ok(Attribute {
-                    key: b"key",
+                    key: QName(b"key"),
                     value: Cow::Borrowed(b"value"),
                 }))
             );
@@ -1620,7 +1621,7 @@ mod html {
             assert_eq!(
                 iter.next(),
                 Some(Ok(Attribute {
-                    key: b"key",
+                    key: QName(b"key"),
                     value: Cow::Borrowed(b"value"),
                 }))
             );
@@ -1636,7 +1637,7 @@ mod html {
             assert_eq!(
                 iter.next(),
                 Some(Ok(Attribute {
-                    key: b"key",
+                    key: QName(b"key"),
                     value: Cow::Borrowed(b"value"),
                 }))
             );
@@ -1652,7 +1653,7 @@ mod html {
             assert_eq!(
                 iter.next(),
                 Some(Ok(Attribute {
-                    key: b"key",
+                    key: QName(b"key"),
                     value: Cow::Borrowed(&[]),
                 }))
             );
@@ -1670,7 +1671,7 @@ mod html {
             assert_eq!(
                 iter.next(),
                 Some(Ok(Attribute {
-                    key: b"'key'",
+                    key: QName(b"'key'"),
                     value: Cow::Borrowed(b"value"),
                 }))
             );
@@ -1688,7 +1689,7 @@ mod html {
             assert_eq!(
                 iter.next(),
                 Some(Ok(Attribute {
-                    key: b"key&jey",
+                    key: QName(b"key&jey"),
                     value: Cow::Borrowed(b"value"),
                 }))
             );
@@ -1721,14 +1722,14 @@ mod html {
             assert_eq!(
                 iter.next(),
                 Some(Ok(Attribute {
-                    key: b"key",
+                    key: QName(b"key"),
                     value: Cow::Borrowed(b"value"),
                 }))
             );
             assert_eq!(
                 iter.next(),
                 Some(Ok(Attribute {
-                    key: b"regular",
+                    key: QName(b"regular"),
                     value: Cow::Borrowed(b"attribute"),
                 }))
             );
@@ -1744,14 +1745,14 @@ mod html {
             assert_eq!(
                 iter.next(),
                 Some(Ok(Attribute {
-                    key: b"key",
+                    key: QName(b"key"),
                     value: Cow::Borrowed(b"value"),
                 }))
             );
             assert_eq!(
                 iter.next(),
                 Some(Ok(Attribute {
-                    key: b"regular",
+                    key: QName(b"regular"),
                     value: Cow::Borrowed(b"attribute"),
                 }))
             );
@@ -1767,14 +1768,14 @@ mod html {
             assert_eq!(
                 iter.next(),
                 Some(Ok(Attribute {
-                    key: b"key",
+                    key: QName(b"key"),
                     value: Cow::Borrowed(b"value"),
                 }))
             );
             assert_eq!(
                 iter.next(),
                 Some(Ok(Attribute {
-                    key: b"regular",
+                    key: QName(b"regular"),
                     value: Cow::Borrowed(b"attribute"),
                 }))
             );
@@ -1790,14 +1791,14 @@ mod html {
             assert_eq!(
                 iter.next(),
                 Some(Ok(Attribute {
-                    key: b"key",
+                    key: QName(b"key"),
                     value: Cow::Borrowed(&[]),
                 }))
             );
             assert_eq!(
                 iter.next(),
                 Some(Ok(Attribute {
-                    key: b"regular",
+                    key: QName(b"regular"),
                     value: Cow::Borrowed(b"attribute"),
                 }))
             );
@@ -1815,14 +1816,14 @@ mod html {
             assert_eq!(
                 iter.next(),
                 Some(Ok(Attribute {
-                    key: b"'key'",
+                    key: QName(b"'key'"),
                     value: Cow::Borrowed(b"value"),
                 }))
             );
             assert_eq!(
                 iter.next(),
                 Some(Ok(Attribute {
-                    key: b"regular",
+                    key: QName(b"regular"),
                     value: Cow::Borrowed(b"attribute"),
                 }))
             );
@@ -1840,14 +1841,14 @@ mod html {
             assert_eq!(
                 iter.next(),
                 Some(Ok(Attribute {
-                    key: b"key&jey",
+                    key: QName(b"key&jey"),
                     value: Cow::Borrowed(b"value"),
                 }))
             );
             assert_eq!(
                 iter.next(),
                 Some(Ok(Attribute {
-                    key: b"regular",
+                    key: QName(b"regular"),
                     value: Cow::Borrowed(b"attribute"),
                 }))
             );
@@ -1865,7 +1866,7 @@ mod html {
             assert_eq!(
                 iter.next(),
                 Some(Ok(Attribute {
-                    key: b"key",
+                    key: QName(b"key"),
                     value: Cow::Borrowed(b"regular='attribute'"),
                 }))
             );
@@ -1881,7 +1882,7 @@ mod html {
             assert_eq!(
                 iter.next(),
                 Some(Ok(Attribute {
-                    key: b"key",
+                    key: QName(b"key"),
                     value: Cow::Borrowed(b"regular="),
                 }))
             );
@@ -1890,7 +1891,7 @@ mod html {
             assert_eq!(
                 iter.next(),
                 Some(Ok(Attribute {
-                    key: b"'attribute'",
+                    key: QName(b"'attribute'"),
                     value: Cow::Borrowed(&[]),
                 }))
             );
@@ -1906,7 +1907,7 @@ mod html {
             assert_eq!(
                 iter.next(),
                 Some(Ok(Attribute {
-                    key: b"key",
+                    key: QName(b"key"),
                     value: Cow::Borrowed(b"regular"),
                 }))
             );
@@ -1915,7 +1916,7 @@ mod html {
             assert_eq!(
                 iter.next(),
                 Some(Ok(Attribute {
-                    key: b"='attribute'",
+                    key: QName(b"='attribute'"),
                     value: Cow::Borrowed(&[]),
                 }))
             );
@@ -1932,7 +1933,7 @@ mod html {
             assert_eq!(
                 iter.next(),
                 Some(Ok(Attribute {
-                    key: b"key",
+                    key: QName(b"key"),
                     value: Cow::Borrowed(b"regular"),
                 }))
             );
@@ -1941,7 +1942,7 @@ mod html {
             assert_eq!(
                 iter.next(),
                 Some(Ok(Attribute {
-                    key: b"=",
+                    key: QName(b"="),
                     value: Cow::Borrowed(&[]),
                 }))
             );
@@ -1950,7 +1951,7 @@ mod html {
             assert_eq!(
                 iter.next(),
                 Some(Ok(Attribute {
-                    key: b"'attribute'",
+                    key: QName(b"'attribute'"),
                     value: Cow::Borrowed(&[]),
                 }))
             );
@@ -1972,7 +1973,7 @@ mod html {
             assert_eq!(
                 iter.next(),
                 Some(Ok(Attribute {
-                    key: b"key",
+                    key: QName(b"key"),
                     value: Cow::Borrowed(b"value"),
                 }))
             );
@@ -1988,7 +1989,7 @@ mod html {
             assert_eq!(
                 iter.next(),
                 Some(Ok(Attribute {
-                    key: b"key",
+                    key: QName(b"key"),
                     value: Cow::Borrowed(b"value"),
                 }))
             );
@@ -2004,7 +2005,7 @@ mod html {
             assert_eq!(
                 iter.next(),
                 Some(Ok(Attribute {
-                    key: b"key",
+                    key: QName(b"key"),
                     value: Cow::Borrowed(b"value"),
                 }))
             );
@@ -2020,7 +2021,7 @@ mod html {
             assert_eq!(
                 iter.next(),
                 Some(Ok(Attribute {
-                    key: b"key",
+                    key: QName(b"key"),
                     value: Cow::Borrowed(&[]),
                 }))
             );
@@ -2038,7 +2039,7 @@ mod html {
             assert_eq!(
                 iter.next(),
                 Some(Ok(Attribute {
-                    key: b"'key'",
+                    key: QName(b"'key'"),
                     value: Cow::Borrowed(b"value"),
                 }))
             );
@@ -2056,7 +2057,7 @@ mod html {
             assert_eq!(
                 iter.next(),
                 Some(Ok(Attribute {
-                    key: b"key&jey",
+                    key: QName(b"key&jey"),
                     value: Cow::Borrowed(b"value"),
                 }))
             );
@@ -2094,7 +2095,7 @@ mod html {
                 assert_eq!(
                     iter.next(),
                     Some(Ok(Attribute {
-                        key: b"key",
+                        key: QName(b"key"),
                         value: Cow::Borrowed(b"value"),
                     }))
                 );
@@ -2102,7 +2103,7 @@ mod html {
                 assert_eq!(
                     iter.next(),
                     Some(Ok(Attribute {
-                        key: b"another",
+                        key: QName(b"another"),
                         value: Cow::Borrowed(b""),
                     }))
                 );
@@ -2119,7 +2120,7 @@ mod html {
                 assert_eq!(
                     iter.next(),
                     Some(Ok(Attribute {
-                        key: b"key",
+                        key: QName(b"key"),
                         value: Cow::Borrowed(b"value"),
                     }))
                 );
@@ -2127,7 +2128,7 @@ mod html {
                 assert_eq!(
                     iter.next(),
                     Some(Ok(Attribute {
-                        key: b"another",
+                        key: QName(b"another"),
                         value: Cow::Borrowed(b""),
                     }))
                 );
@@ -2144,7 +2145,7 @@ mod html {
                 assert_eq!(
                     iter.next(),
                     Some(Ok(Attribute {
-                        key: b"key",
+                        key: QName(b"key"),
                         value: Cow::Borrowed(b"value"),
                     }))
                 );
@@ -2152,7 +2153,7 @@ mod html {
                 assert_eq!(
                     iter.next(),
                     Some(Ok(Attribute {
-                        key: b"another",
+                        key: QName(b"another"),
                         value: Cow::Borrowed(b""),
                     }))
                 );
@@ -2169,7 +2170,7 @@ mod html {
                 assert_eq!(
                     iter.next(),
                     Some(Ok(Attribute {
-                        key: b"key",
+                        key: QName(b"key"),
                         value: Cow::Borrowed(b"value"),
                     }))
                 );
@@ -2177,7 +2178,7 @@ mod html {
                 assert_eq!(
                     iter.next(),
                     Some(Ok(Attribute {
-                        key: b"another",
+                        key: QName(b"another"),
                         value: Cow::Borrowed(b""),
                     }))
                 );
@@ -2200,21 +2201,21 @@ mod html {
                 assert_eq!(
                     iter.next(),
                     Some(Ok(Attribute {
-                        key: b"key",
+                        key: QName(b"key"),
                         value: Cow::Borrowed(b"value"),
                     }))
                 );
                 assert_eq!(
                     iter.next(),
                     Some(Ok(Attribute {
-                        key: b"key",
+                        key: QName(b"key"),
                         value: Cow::Borrowed(b"dup"),
                     }))
                 );
                 assert_eq!(
                     iter.next(),
                     Some(Ok(Attribute {
-                        key: b"another",
+                        key: QName(b"another"),
                         value: Cow::Borrowed(b""),
                     }))
                 );
@@ -2231,21 +2232,21 @@ mod html {
                 assert_eq!(
                     iter.next(),
                     Some(Ok(Attribute {
-                        key: b"key",
+                        key: QName(b"key"),
                         value: Cow::Borrowed(b"value"),
                     }))
                 );
                 assert_eq!(
                     iter.next(),
                     Some(Ok(Attribute {
-                        key: b"key",
+                        key: QName(b"key"),
                         value: Cow::Borrowed(b"dup"),
                     }))
                 );
                 assert_eq!(
                     iter.next(),
                     Some(Ok(Attribute {
-                        key: b"another",
+                        key: QName(b"another"),
                         value: Cow::Borrowed(b""),
                     }))
                 );
@@ -2262,21 +2263,21 @@ mod html {
                 assert_eq!(
                     iter.next(),
                     Some(Ok(Attribute {
-                        key: b"key",
+                        key: QName(b"key"),
                         value: Cow::Borrowed(b"value"),
                     }))
                 );
                 assert_eq!(
                     iter.next(),
                     Some(Ok(Attribute {
-                        key: b"key",
+                        key: QName(b"key"),
                         value: Cow::Borrowed(b"dup"),
                     }))
                 );
                 assert_eq!(
                     iter.next(),
                     Some(Ok(Attribute {
-                        key: b"another",
+                        key: QName(b"another"),
                         value: Cow::Borrowed(b""),
                     }))
                 );
@@ -2293,21 +2294,21 @@ mod html {
                 assert_eq!(
                     iter.next(),
                     Some(Ok(Attribute {
-                        key: b"key",
+                        key: QName(b"key"),
                         value: Cow::Borrowed(b"value"),
                     }))
                 );
                 assert_eq!(
                     iter.next(),
                     Some(Ok(Attribute {
-                        key: b"key",
+                        key: QName(b"key"),
                         value: Cow::Borrowed(&[]),
                     }))
                 );
                 assert_eq!(
                     iter.next(),
                     Some(Ok(Attribute {
-                        key: b"another",
+                        key: QName(b"another"),
                         value: Cow::Borrowed(b""),
                     }))
                 );
@@ -2324,28 +2325,28 @@ mod html {
         assert_eq!(
             iter.next(),
             Some(Ok(Attribute {
-                key: b"a",
+                key: QName(b"a"),
                 value: Cow::Borrowed(b"a"),
             }))
         );
         assert_eq!(
             iter.next(),
             Some(Ok(Attribute {
-                key: b"b",
+                key: QName(b"b"),
                 value: Cow::Borrowed(b"b"),
             }))
         );
         assert_eq!(
             iter.next(),
             Some(Ok(Attribute {
-                key: b"c",
+                key: QName(b"c"),
                 value: Cow::Borrowed(br#"cc"cc"#),
             }))
         );
         assert_eq!(
             iter.next(),
             Some(Ok(Attribute {
-                key: b"d",
+                key: QName(b"d"),
                 value: Cow::Borrowed(b"dd'dd"),
             }))
         );
diff --git a/src/events/mod.rs b/src/events/mod.rs
index 0098dab..227d55d 100644
--- a/src/events/mod.rs
+++ b/src/events/mod.rs
@@ -41,6 +41,7 @@ use encoding_rs::Encoding;
 use std::{borrow::Cow, collections::HashMap, io::BufRead, ops::Deref, str::from_utf8};
 
 use crate::escape::{do_unescape, escape, partial_escape};
+use crate::name::{LocalName, QName};
 use crate::utils::write_cow_string;
 use crate::{errors::Error, errors::Result, reader::Reader};
 use attributes::{Attribute, Attributes};
@@ -48,8 +49,6 @@ use attributes::{Attribute, Attributes};
 #[cfg(feature = "serialize")]
 use crate::escape::EscapeError;
 
-use memchr;
-
 /// Opening tag data (`Event::Start`), with optional attributes.
 ///
 /// `<name attr="value">`.
@@ -156,22 +155,22 @@ impl<'a> BytesStart<'a> {
 
     /// Creates new paired close tag
     pub fn to_end(&self) -> BytesEnd {
-        BytesEnd::borrowed(self.name())
+        BytesEnd::borrowed(self.name().into_inner())
     }
 
-    /// Gets the undecoded raw tag name as a `&[u8]`.
+    /// Gets the undecoded raw tag name, as present in the input stream.
     #[inline]
-    pub fn name(&self) -> &[u8] {
-        &self.buf[..self.name_len]
+    pub fn name(&self) -> QName {
+        QName(&self.buf[..self.name_len])
     }
 
-    /// Gets the undecoded raw local tag name (excluding namespace) as a `&[u8]`.
+    /// Gets the undecoded raw local tag name (excluding namespace) as present
+    /// in the input stream.
     ///
     /// All content up to and including the first `:` character is removed from the tag name.
     #[inline]
-    pub fn local_name(&self) -> &[u8] {
-        let name = self.name();
-        memchr::memchr(b':', name).map_or(name, |i| &name[i + 1..])
+    pub fn local_name(&self) -> LocalName {
+        self.name().into()
     }
 
     /// Gets the unescaped tag name.
@@ -325,7 +324,7 @@ impl<'a> BytesStart<'a> {
         let a = attr.into();
         let bytes = self.buf.to_mut();
         bytes.push(b' ');
-        bytes.extend_from_slice(a.key);
+        bytes.extend_from_slice(a.key.as_ref());
         bytes.extend_from_slice(b"=\"");
         bytes.extend_from_slice(&*a.value);
         bytes.push(b'"');
@@ -361,7 +360,7 @@ impl<'a> BytesStart<'a> {
     ) -> Result<Option<Attribute<'a>>> {
         for a in self.attributes() {
             let a = a?;
-            if a.key == attr_name.as_ref() {
+            if a.key.as_ref() == attr_name.as_ref() {
                 return Ok(Some(a));
             }
         }
@@ -449,10 +448,10 @@ impl<'a> BytesDecl<'a> {
     pub fn version(&self) -> Result<Cow<[u8]>> {
         // The version *must* be the first thing in the declaration.
         match self.element.attributes().with_checks(false).next() {
-            Some(Ok(a)) if a.key == b"version" => Ok(a.value),
+            Some(Ok(a)) if a.key.as_ref() == b"version" => Ok(a.value),
             // first attribute was not "version"
             Some(Ok(a)) => {
-                let found = from_utf8(a.key).map_err(Error::Utf8)?.to_string();
+                let found = from_utf8(a.key.as_ref()).map_err(Error::Utf8)?.to_string();
                 Err(Error::XmlDeclWithoutVersion(Some(found)))
             }
             // error parsing attributes
@@ -641,22 +640,19 @@ impl<'a> BytesEnd<'a> {
         }
     }
 
-    /// Gets `BytesEnd` event name
+    /// Gets the undecoded raw tag name, as present in the input stream.
     #[inline]
-    pub fn name(&self) -> &[u8] {
-        &*self.name
+    pub fn name(&self) -> QName {
+        QName(&*self.name)
     }
 
-    /// local name (excluding namespace) as &[u8] (without eventual attributes)
-    /// returns the name() with any leading namespace removed (all content up to
-    /// and including the first ':' character)
+    /// Gets the undecoded raw local tag name (excluding namespace) as present
+    /// in the input stream.
+    ///
+    /// All content up to and including the first `:` character is removed from the tag name.
     #[inline]
-    pub fn local_name(&self) -> &[u8] {
-        if let Some(i) = self.name().iter().position(|b| *b == b':') {
-            &self.name()[i + 1..]
-        } else {
-            self.name()
-        }
+    pub fn local_name(&self) -> LocalName {
+        self.name().into()
     }
 }
 
@@ -1179,12 +1175,12 @@ mod test {
         loop {
             match rdr.read_event(&mut buf).expect("unable to read xml event") {
                 Event::Start(ref e) => parsed_local_names.push(
-                    from_utf8(e.local_name())
+                    from_utf8(e.local_name().as_ref())
                         .expect("unable to build str from local_name")
                         .to_string(),
                 ),
                 Event::End(ref e) => parsed_local_names.push(
-                    from_utf8(e.local_name())
+                    from_utf8(e.local_name().as_ref())
                         .expect("unable to build str from local_name")
                         .to_string(),
                 ),
@@ -1206,21 +1202,21 @@ mod test {
     fn bytestart_create() {
         let b = BytesStart::owned_name("test");
         assert_eq!(b.len(), 4);
-        assert_eq!(b.name(), b"test");
+        assert_eq!(b.name(), QName(b"test"));
     }
 
     #[test]
     fn bytestart_set_name() {
         let mut b = BytesStart::owned_name("test");
         assert_eq!(b.len(), 4);
-        assert_eq!(b.name(), b"test");
+        assert_eq!(b.name(), QName(b"test"));
         assert_eq!(b.attributes_raw(), b"");
         b.push_attribute(("x", "a"));
         assert_eq!(b.len(), 10);
         assert_eq!(b.attributes_raw(), b" x=\"a\"");
         b.set_name(b"g");
         assert_eq!(b.len(), 7);
-        assert_eq!(b.name(), b"g");
+        assert_eq!(b.name(), QName(b"g"));
     }
 
     #[test]
@@ -1231,6 +1227,6 @@ mod test {
         b.clear_attributes();
         assert!(b.attributes().next().is_none());
         assert_eq!(b.len(), 4);
-        assert_eq!(b.name(), b"test");
+        assert_eq!(b.name(), QName(b"test"));
     }
 }
diff --git a/src/lib.rs b/src/lib.rs
index 3e89a75..122520f 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -50,7 +50,7 @@
 //!         Ok(Event::Start(ref e)) => {
 //!         // for namespaced:
 //!         // Ok((ref namespace_value, Event::Start(ref e)))
-//!             match e.name() {
+//!             match e.name().as_ref() {
 //!                 b"tag1" => println!("attributes values: {:?}",
 //!                                     e.attributes().map(|a| a.unwrap().value)
 //!                                     .collect::<Vec<_>>()),
@@ -87,7 +87,7 @@
 //! let mut buf = Vec::new();
 //! loop {
 //!     match reader.read_event(&mut buf) {
-//!         Ok(Event::Start(ref e)) if e.name() == b"this_tag" => {
+//!         Ok(Event::Start(ref e)) if e.name().as_ref() == b"this_tag" => {
 //!
 //!             // crates a new element ... alternatively we could reuse `e` by calling
 //!             // `e.into_owned()`
@@ -102,7 +102,7 @@
 //!             // writes the event to the writer
 //!             assert!(writer.write_event(Event::Start(elem)).is_ok());
 //!         },
-//!         Ok(Event::End(ref e)) if e.name() == b"this_tag" => {
+//!         Ok(Event::End(ref e)) if e.name().as_ref() == b"this_tag" => {
 //!             assert!(writer.write_event(Event::End(BytesEnd::borrowed(b"my_elem"))).is_ok());
 //!         },
 //!         Ok(Event::Eof) => break,
@@ -143,6 +143,7 @@ pub mod escape {
     pub use crate::escapei::{escape, partial_escape, unescape, unescape_with};
 }
 pub mod events;
+pub mod name;
 mod reader;
 #[cfg(feature = "serialize")]
 pub mod se;
diff --git a/src/name.rs b/src/name.rs
new file mode 100644
index 0000000..83c80d0
--- /dev/null
+++ b/src/name.rs
@@ -0,0 +1,800 @@
+//! Module for handling names according to the W3C [Namespaces in XML 1.1 (Second Edition)][spec]
+//! specification
+//!
+//! [spec]: https://www.w3.org/TR/xml-names11
+
+use crate::errors::{Error, Result};
+use crate::events::attributes::Attribute;
+use crate::events::BytesStart;
+use crate::utils::write_byte_string;
+use memchr::memchr;
+use std::convert::TryFrom;
+use std::fmt::{self, Debug, Formatter};
+
+/// A [qualified name] of an element or an attribute, including an optional
+/// namespace [prefix](Prefix) and a [local name](LocalName).
+///
+/// [qualified name]: https://www.w3.org/TR/xml-names11/#dt-qualname
+#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
+pub struct QName<'a>(pub &'a [u8]);
+impl<'a> QName<'a> {
+    /// Converts this name to an internal slice representation.
+    #[inline(always)]
+    pub fn into_inner(self) -> &'a [u8] {
+        self.0
+    }
+
+    /// Returns local part of this qualified name.
+    ///
+    /// All content up to and including the first `:` character is removed from
+    /// the tag name.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// # use quick_xml::name::QName;
+    /// let simple = QName(b"simple-name");
+    /// assert_eq!(simple.local_name().as_ref(), b"simple-name");
+    ///
+    /// let qname = QName(b"namespace:simple-name");
+    /// assert_eq!(qname.local_name().as_ref(), b"simple-name");
+    /// ```
+    pub fn local_name(&self) -> LocalName<'a> {
+        LocalName(self.index().map_or(self.0, |i| &self.0[i + 1..]))
+    }
+
+    /// Returns namespace part of this qualified name or `None` if namespace part
+    /// is not defined (symbol `':'` not found).
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// # use std::convert::AsRef;
+    /// # use quick_xml::name::QName;
+    /// let simple = QName(b"simple-name");
+    /// assert_eq!(simple.prefix(), None);
+    ///
+    /// let qname = QName(b"prefix:simple-name");
+    /// assert_eq!(qname.prefix().as_ref().map(|n| n.as_ref()), Some(b"prefix".as_ref()));
+    /// ```
+    pub fn prefix(&self) -> Option<Prefix<'a>> {
+        self.index().map(|i| Prefix(&self.0[..i]))
+    }
+
+    /// The same as `(qname.local_name(), qname.prefix())`, but does only one
+    /// lookup for a `':'` symbol.
+    pub fn decompose(&self) -> (LocalName<'a>, Option<Prefix<'a>>) {
+        match self.index() {
+            None => (LocalName(self.0), None),
+            Some(i) => (LocalName(&self.0[i + 1..]), Some(Prefix(&self.0[..i]))),
+        }
+    }
+
+    /// If that `QName` represents `"xmlns"` series of names, returns `Some`,
+    /// otherwise `None` is returned.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// # use quick_xml::name::{QName, PrefixDeclaration};
+    /// let qname = QName(b"xmlns");
+    /// assert_eq!(qname.as_namespace_binding(), Some(PrefixDeclaration::Default));
+    ///
+    /// let qname = QName(b"xmlns:prefix");
+    /// assert_eq!(qname.as_namespace_binding(), Some(PrefixDeclaration::Named(b"prefix")));
+    ///
+    /// // Be aware that this method does not check the validity of the prefix - it can be empty!
+    /// let qname = QName(b"xmlns:");
+    /// assert_eq!(qname.as_namespace_binding(), Some(PrefixDeclaration::Named(b"")));
+    ///
+    /// let qname = QName(b"other-name");
+    /// assert_eq!(qname.as_namespace_binding(), None);
+    ///
+    /// // https://www.w3.org/TR/xml-names11/#xmlReserved
+    /// let qname = QName(b"xmlns-reserved-name");
+    /// assert_eq!(qname.as_namespace_binding(), None);
+    /// ```
+    pub fn as_namespace_binding(&self) -> Option<PrefixDeclaration<'a>> {
+        if self.0.starts_with(b"xmlns") {
+            return match self.0.get(5) {
+                None => Some(PrefixDeclaration::Default),
+                Some(&b':') => Some(PrefixDeclaration::Named(&self.0[6..])),
+                _ => None,
+            };
+        }
+        None
+    }
+
+    /// Returns the index in the name where prefix ended
+    #[inline(always)]
+    fn index(&self) -> Option<usize> {
+        memchr(b':', self.0)
+    }
+}
+impl<'a> Debug for QName<'a> {
+    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+        write!(f, "QName(")?;
+        write_byte_string(f, self.0)?;
+        write!(f, ")")
+    }
+}
+impl<'a> AsRef<[u8]> for QName<'a> {
+    #[inline]
+    fn as_ref(&self) -> &[u8] {
+        self.0
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/// A [local (unqualified) name] of an element or an attribute, i.e. a name
+/// without [prefix](Prefix).
+///
+/// [local (unqualified) name]: https://www.w3.org/TR/xml-names11/#dt-localname
+#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
+pub struct LocalName<'a>(&'a [u8]);
+impl<'a> LocalName<'a> {
+    /// Converts this name to an internal slice representation.
+    #[inline(always)]
+    pub fn into_inner(self) -> &'a [u8] {
+        self.0
+    }
+}
+impl<'a> Debug for LocalName<'a> {
+    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+        write!(f, "LocalName(")?;
+        write_byte_string(f, self.0)?;
+        write!(f, ")")
+    }
+}
+impl<'a> AsRef<[u8]> for LocalName<'a> {
+    #[inline]
+    fn as_ref(&self) -> &[u8] {
+        self.0
+    }
+}
+impl<'a> From<QName<'a>> for LocalName<'a> {
+    /// Creates `LocalName` from a [`QName`]
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// # use quick_xml::name::{LocalName, QName};
+    ///
+    /// let local: LocalName = QName(b"unprefixed").into();
+    /// assert_eq!(local.as_ref(), b"unprefixed");
+    ///
+    /// let local: LocalName = QName(b"some:prefix").into();
+    /// assert_eq!(local.as_ref(), b"prefix");
+    /// ```
+    #[inline]
+    fn from(name: QName<'a>) -> Self {
+        Self(name.index().map_or(&name.0, |i| &name.0[i + 1..]))
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/// A [namespace prefix] part of the [qualified name](QName) of an element tag
+/// or an attribute: a `prefix` in `<prefix:local-element-name>` or
+/// `prefix:local-attribute-name="attribute value"`.
+///
+/// [namespace prefix]: https://www.w3.org/TR/xml-names11/#dt-prefix
+#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
+pub struct Prefix<'a>(&'a [u8]);
+impl<'a> Prefix<'a> {
+    /// Extracts internal slice
+    #[inline(always)]
+    pub fn into_inner(self) -> &'a [u8] {
+        self.0
+    }
+}
+impl<'a> Debug for Prefix<'a> {
+    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+        write!(f, "Prefix(")?;
+        write_byte_string(f, self.0)?;
+        write!(f, ")")
+    }
+}
+impl<'a> AsRef<[u8]> for Prefix<'a> {
+    #[inline]
+    fn as_ref(&self) -> &[u8] {
+        self.0
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/// A namespace prefix declaration, `xmlns` or `xmlns:<name>`, as defined in
+/// [XML Schema specification](https://www.w3.org/TR/xml-names/#ns-decl)
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub enum PrefixDeclaration<'a> {
+    /// XML attribute binds a default namespace. Corresponds to `xmlns` in in `xmlns="..."`
+    Default,
+    /// XML attribute binds a specified prefix to a namespace. Corresponds to a
+    /// `prefix` in `xmlns:prefix="..."`, which is stored as payload of this variant.
+    Named(&'a [u8]),
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/// A [namespace name] that is declared in a `xmlns[:prefix]="namespace name"`.
+///
+/// [namespace name]: https://www.w3.org/TR/xml-names11/#dt-NSName
+#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
+pub struct Namespace<'a>(pub &'a [u8]);
+impl<'a> Namespace<'a> {
+    /// Converts this namespace to an internal slice representation.
+    ///
+    /// This is [non-normalized] attribute value, i.e. any entity references is
+    /// not expanded and space characters are not removed. This means, that
+    /// different byte slices, returned from this method, can represent the same
+    /// namespace and would be treated by parser as identical.
+    ///
+    /// For example, if the entity **eacute** has been defined to be **é**,
+    /// the empty tags below all contain namespace declarations binding the
+    /// prefix `p` to the same [IRI reference], `http://example.org/rosé`.
+    ///
+    /// ```xml
+    /// <p:foo xmlns:p="http://example.org/rosé" />
+    /// <p:foo xmlns:p="http://example.org/ros&#xe9;" />
+    /// <p:foo xmlns:p="http://example.org/ros&#xE9;" />
+    /// <p:foo xmlns:p="http://example.org/ros&#233;" />
+    /// <p:foo xmlns:p="http://example.org/ros&eacute;" />
+    /// ```
+    ///
+    /// This is because XML entity references are expanded during attribute value
+    /// normalization.
+    ///
+    /// [non-normalized]: https://www.w3.org/TR/REC-xml/#AVNormalize
+    /// [IRI reference]: https://datatracker.ietf.org/doc/html/rfc3987
+    #[inline(always)]
+    pub fn into_inner(self) -> &'a [u8] {
+        self.0
+    }
+    //TODO: implement value normalization and use it when comparing namespaces
+}
+impl<'a> Debug for Namespace<'a> {
+    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+        write!(f, "Namespace(")?;
+        write_byte_string(f, self.0)?;
+        write!(f, ")")
+    }
+}
+impl<'a> AsRef<[u8]> for Namespace<'a> {
+    #[inline]
+    fn as_ref(&self) -> &[u8] {
+        self.0
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/// Result of [prefix] resolution which creates by [`Reader::attribute_namespace`],
+/// [`Reader::event_namespace`] and [`Reader::read_namespaced_event`] methods.
+///
+/// [prefix]: Prefix
+/// [`Reader::attribute_namespace`]: crate::reader::Reader::attribute_namespace
+/// [`Reader::event_namespace`]: crate::reader::Reader::event_namespace
+/// [`Reader::read_namespaced_event`]: crate::reader::Reader::read_namespaced_event
+#[derive(Clone, PartialEq, Eq, Hash)]
+pub enum ResolveResult<'ns> {
+    /// Qualified name does not contain prefix, and resolver does not define
+    /// default namespace, so name is not bound to any namespace
+    Unbound,
+    /// [`Prefix`] resolved to the specified namespace
+    Bound(Namespace<'ns>),
+    /// Specified prefix was not found in scope
+    Unknown(Vec<u8>),
+}
+impl<'ns> Debug for ResolveResult<'ns> {
+    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+        match self {
+            Self::Unbound => write!(f, "Unbound"),
+            Self::Bound(ns) => write!(f, "Bound({:?})", ns),
+            Self::Unknown(p) => {
+                write!(f, "Unknown(")?;
+                write_byte_string(f, p)?;
+                write!(f, ")")
+            }
+        }
+    }
+}
+
+impl<'ns> TryFrom<ResolveResult<'ns>> for Option<Namespace<'ns>> {
+    type Error = Error;
+
+    /// Try to convert this result to an optional namespace and returns
+    /// [`Error::UnknownPrefix`] if this result represents unknown prefix
+    fn try_from(result: ResolveResult<'ns>) -> Result<Self> {
+        use ResolveResult::*;
+
+        match result {
+            Unbound => Ok(None),
+            Bound(ns) => Ok(Some(ns)),
+            Unknown(p) => Err(Error::UnknownPrefix(p)),
+        }
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/// An entry that contains index into the buffer with namespace bindings.
+///
+/// Defines a mapping from *[namespace prefix]* to *[namespace name]*.
+/// If prefix is empty, defines a *default namespace* binding that applies to
+/// unprefixed element names (unprefixed attribute names do not bind to any
+/// namespace and they processing is dependent on the element in which their
+/// defined).
+///
+/// [namespace prefix]: https://www.w3.org/TR/xml-names11/#dt-prefix
+/// [namespace name]: https://www.w3.org/TR/xml-names11/#dt-NSName
+#[derive(Debug, Clone)]
+struct NamespaceEntry {
+    /// Index of the namespace in the buffer
+    start: usize,
+    /// Length of the prefix
+    /// * if greater than zero, then binds this namespace to the slice
+    ///   `[start..start + prefix_len]` in the buffer.
+    /// * else defines the current default namespace.
+    prefix_len: usize,
+    /// The length of a namespace name (the URI) of this namespace declaration.
+    /// Name started just after prefix and extend for `value_len` bytes.
+    ///
+    /// The XML standard [specifies] that an empty namespace value 'removes' a namespace declaration
+    /// for the extent of its scope. For prefix declarations that's not very interesting, but it is
+    /// vital for default namespace declarations. With `xmlns=""` you can revert back to the default
+    /// behaviour of leaving unqualified element names unqualified.
+    ///
+    /// [specifies]: https://www.w3.org/TR/xml-names11/#scoping
+    value_len: usize,
+    /// Level of nesting at which this namespace was declared. The declaring element is included,
+    /// i.e., a declaration on the document root has `level = 1`.
+    /// This is used to pop the namespace when the element gets closed.
+    level: i32,
+}
+
+impl NamespaceEntry {
+    /// Get the namespace prefix, bound to this namespace declaration, or `None`,
+    /// if this declaration is for default namespace (`xmlns="..."`).
+    #[inline]
+    fn prefix<'b>(&self, ns_buffer: &'b [u8]) -> Option<Prefix<'b>> {
+        if self.prefix_len == 0 {
+            None
+        } else {
+            Some(Prefix(&ns_buffer[self.start..self.start + self.prefix_len]))
+        }
+    }
+
+    /// Gets the namespace name (the URI) slice out of namespace buffer
+    ///
+    /// Returns `None` if namespace for this prefix was explicitly removed from
+    /// scope, using `xmlns[:prefix]=""`
+    #[inline]
+    fn namespace<'ns>(&self, buffer: &'ns [u8]) -> ResolveResult<'ns> {
+        if self.value_len == 0 {
+            ResolveResult::Unbound
+        } else {
+            let start = self.start + self.prefix_len;
+            ResolveResult::Bound(Namespace(&buffer[start..start + self.value_len]))
+        }
+    }
+}
+
+/// A namespace management buffer.
+///
+/// Holds all internal logic to push/pop namespaces with their levels.
+#[derive(Debug, Default, Clone)]
+pub(crate) struct NamespaceResolver {
+    /// A stack of namespace bindings to prefixes that currently in scope
+    bindings: Vec<NamespaceEntry>,
+    /// The number of open tags at the moment. We need to keep track of this to know which namespace
+    /// declarations to remove when we encounter an `End` event.
+    nesting_level: i32,
+}
+
+impl NamespaceResolver {
+    /// Begins a new scope and add to it all [namespace bindings] that found in
+    /// the specified start element.
+    ///
+    /// [namespace binding]: https://www.w3.org/TR/xml-names11/#dt-NSDecl
+    pub fn push(&mut self, start: &BytesStart, buffer: &mut Vec<u8>) {
+        self.nesting_level += 1;
+        let level = self.nesting_level;
+        // adds new namespaces for attributes starting with 'xmlns:' and for the 'xmlns'
+        // (default namespace) attribute.
+        for a in start.attributes().with_checks(false) {
+            if let Ok(Attribute { key: k, value: v }) = a {
+                match k.as_namespace_binding() {
+                    Some(PrefixDeclaration::Default) => {
+                        let start = buffer.len();
+                        buffer.extend_from_slice(&*v);
+                        self.bindings.push(NamespaceEntry {
+                            start,
+                            prefix_len: 0,
+                            value_len: v.len(),
+                            level,
+                        });
+                    }
+                    Some(PrefixDeclaration::Named(prefix)) => {
+                        let start = buffer.len();
+                        buffer.extend_from_slice(prefix);
+                        buffer.extend_from_slice(&*v);
+                        self.bindings.push(NamespaceEntry {
+                            start,
+                            prefix_len: prefix.len(),
+                            value_len: v.len(),
+                            level,
+                        });
+                    }
+                    None => {}
+                }
+            } else {
+                break;
+            }
+        }
+    }
+
+    /// Ends a top-most scope by popping all [namespace binding], that was added by
+    /// last call to [`Self::push()`].
+    ///
+    /// [namespace binding]: https://www.w3.org/TR/xml-names11/#dt-NSDecl
+    pub fn pop(&mut self, buffer: &mut Vec<u8>) {
+        self.nesting_level -= 1;
+        let current_level = self.nesting_level;
+        // from the back (most deeply nested scope), look for the first scope that is still valid
+        match self.bindings.iter().rposition(|n| n.level <= current_level) {
+            // none of the namespaces are valid, remove all of them
+            None => {
+                buffer.clear();
+                self.bindings.clear();
+            }
+            // drop all namespaces past the last valid namespace
+            Some(last_valid_pos) => {
+                if let Some(len) = self.bindings.get(last_valid_pos + 1).map(|n| n.start) {
+                    buffer.truncate(len);
+                    self.bindings.truncate(last_valid_pos + 1);
+                }
+            }
+        }
+    }
+
+    /// Resolves a potentially qualified **element name** or **attribute name**
+    /// into (namespace name, local name).
+    ///
+    /// *Qualified* names have the form `prefix:local-name` where the `prefix` is
+    /// defined on any containing XML element via `xmlns:prefix="the:namespace:uri"`.
+    /// The namespace prefix can be defined on the same element as the element or
+    /// attribute in question.
+    ///
+    /// *Unqualified* attribute names do *not* inherit the current *default namespace*.
+    ///
+    /// # Lifetimes
+    ///
+    /// - `'n`: lifetime of an attribute or an element name
+    /// - `'ns`: lifetime of a namespaces buffer, where all found namespaces are stored
+    #[inline]
+    pub fn resolve<'n, 'ns>(
+        &self,
+        name: QName<'n>,
+        buffer: &'ns [u8],
+        use_default: bool,
+    ) -> (ResolveResult<'ns>, LocalName<'n>) {
+        let (local_name, prefix) = name.decompose();
+        (self.resolve_prefix(prefix, buffer, use_default), local_name)
+    }
+
+    /// Finds a [namespace name] for a given qualified **element name**, borrow
+    /// it from the specified buffer.
+    ///
+    /// Returns `None`, if:
+    /// - name is unqualified
+    /// - prefix not found in the current scope
+    /// - prefix was [unbound] using `xmlns:prefix=""`
+    ///
+    /// # Lifetimes
+    ///
+    /// - `'ns`: lifetime of a namespaces buffer, where all found namespaces are stored
+    ///
+    /// [namespace name]: https://www.w3.org/TR/xml-names11/#dt-NSName
+    /// [unbound]: https://www.w3.org/TR/xml-names11/#scoping
+    #[inline]
+    pub fn find<'ns>(&self, element_name: QName, buffer: &'ns [u8]) -> ResolveResult<'ns> {
+        self.resolve_prefix(element_name.prefix(), buffer, true)
+    }
+
+    fn resolve_prefix<'ns>(
+        &self,
+        prefix: Option<Prefix>,
+        buffer: &'ns [u8],
+        use_default: bool,
+    ) -> ResolveResult<'ns> {
+        self.bindings
+            .iter()
+            // Find the last defined binding that corresponds to the given prefix
+            .rev()
+            .find_map(|n| match (n.prefix(buffer), prefix) {
+                // This is default namespace definition and name has no explicit prefix
+                (None, None) if use_default => Some(n.namespace(buffer)),
+                (None, None) => Some(ResolveResult::Unbound),
+
+                // One part has prefix but other is not -> skip
+                (None, Some(_)) => None,
+                (Some(_), None) => None,
+
+                // Prefixes does not match -> skip
+                (Some(definition), Some(usage)) if definition != usage => None,
+
+                // Prefixes the same, entry defines binding reset (corresponds to `xmlns:p=""`)
+                _ if n.value_len == 0 => Some(Self::maybe_unknown(prefix)),
+                // Prefixes the same, returns corresponding namespace
+                _ => Some(n.namespace(buffer)),
+            })
+            .unwrap_or_else(|| Self::maybe_unknown(prefix))
+    }
+
+    #[inline]
+    fn maybe_unknown(prefix: Option<Prefix>) -> ResolveResult<'static> {
+        match prefix {
+            Some(p) => ResolveResult::Unknown(p.into_inner().to_vec()),
+            None => ResolveResult::Unbound,
+        }
+    }
+}
+
+#[cfg(test)]
+mod namespaces {
+    use super::*;
+    use pretty_assertions::assert_eq;
+    use ResolveResult::*;
+
+    /// Unprefixed attribute names (resolved with `false` flag) never have a namespace
+    /// according to <https://www.w3.org/TR/xml-names11/#defaulting>:
+    ///
+    /// > A default namespace declaration applies to all unprefixed element names
+    /// > within its scope. Default namespace declarations do not apply directly
+    /// > to attribute names; the interpretation of unprefixed attributes is
+    /// > determined by the element on which they appear.
+    mod unprefixed {
+        use super::*;
+        use pretty_assertions::assert_eq;
+
+        /// Basic tests that checks that basic resolver functionality is working
+        #[test]
+        fn basic() {
+            let name = QName(b"simple");
+            let ns = Namespace(b"default");
+
+            let mut resolver = NamespaceResolver::default();
+            let mut buffer = Vec::new();
+
+            resolver.push(&BytesStart::borrowed(b" xmlns='default'", 0), &mut buffer);
+            assert_eq!(buffer, b"default");
+
+            // Check that tags without namespaces does not change result
+            resolver.push(&BytesStart::borrowed(b"", 0), &mut buffer);
+            assert_eq!(buffer, b"default");
+            resolver.pop(&mut buffer);
+
+            assert_eq!(buffer, b"default");
+            assert_eq!(
+                resolver.resolve(name, &buffer, true),
+                (Bound(ns), LocalName(b"simple"))
+            );
+            assert_eq!(
+                resolver.resolve(name, &buffer, false),
+                (Unbound, LocalName(b"simple"))
+            );
+            assert_eq!(resolver.find(name, &buffer), Bound(ns));
+        }
+
+        /// Test adding a second level of namespaces, which replaces the previous binding
+        #[test]
+        fn override_namespace() {
+            let name = QName(b"simple");
+            let old_ns = Namespace(b"old");
+            let new_ns = Namespace(b"new");
+
+            let mut resolver = NamespaceResolver::default();
+            let mut buffer = Vec::new();
+
+            resolver.push(&BytesStart::borrowed(b" xmlns='old'", 0), &mut buffer);
+            resolver.push(&BytesStart::borrowed(b" xmlns='new'", 0), &mut buffer);
+
+            assert_eq!(buffer, b"oldnew");
+            assert_eq!(
+                resolver.resolve(name, &buffer, true),
+                (Bound(new_ns), LocalName(b"simple"))
+            );
+            assert_eq!(
+                resolver.resolve(name, &buffer, false),
+                (Unbound, LocalName(b"simple"))
+            );
+            assert_eq!(resolver.find(name, &buffer), Bound(new_ns));
+
+            resolver.pop(&mut buffer);
+            assert_eq!(buffer, b"old");
+            assert_eq!(
+                resolver.resolve(name, &buffer, true),
+                (Bound(old_ns), LocalName(b"simple"))
+            );
+            assert_eq!(
+                resolver.resolve(name, &buffer, false),
+                (Unbound, LocalName(b"simple"))
+            );
+            assert_eq!(resolver.find(name, &buffer), Bound(old_ns));
+        }
+
+        /// Test adding a second level of namespaces, which reset the previous binding
+        /// to not bound state by specifying an empty namespace name.
+        ///
+        /// See <https://www.w3.org/TR/xml-names11/#scoping>
+        #[test]
+        fn reset() {
+            let name = QName(b"simple");
+            let old_ns = Namespace(b"old");
+
+            let mut resolver = NamespaceResolver::default();
+            let mut buffer = Vec::new();
+
+            resolver.push(&BytesStart::borrowed(b" xmlns='old'", 0), &mut buffer);
+            resolver.push(&BytesStart::borrowed(b" xmlns=''", 0), &mut buffer);
+
+            assert_eq!(buffer, b"old");
+            assert_eq!(
+                resolver.resolve(name, &buffer, true),
+                (Unbound, LocalName(b"simple"))
+            );
+            assert_eq!(
+                resolver.resolve(name, &buffer, false),
+                (Unbound, LocalName(b"simple"))
+            );
+            assert_eq!(resolver.find(name, &buffer), Unbound);
+
+            resolver.pop(&mut buffer);
+            assert_eq!(buffer, b"old");
+            assert_eq!(
+                resolver.resolve(name, &buffer, true),
+                (Bound(old_ns), LocalName(b"simple"))
+            );
+            assert_eq!(
+                resolver.resolve(name, &buffer, false),
+                (Unbound, LocalName(b"simple"))
+            );
+            assert_eq!(resolver.find(name, &buffer), Bound(old_ns));
+        }
+    }
+
+    mod declared_prefix {
+        use super::*;
+        use pretty_assertions::assert_eq;
+
+        /// Basic tests that checks that basic resolver functionality is working
+        #[test]
+        fn basic() {
+            let name = QName(b"p:with-declared-prefix");
+            let ns = Namespace(b"default");
+
+            let mut resolver = NamespaceResolver::default();
+            let mut buffer = Vec::new();
+
+            resolver.push(&BytesStart::borrowed(b" xmlns:p='default'", 0), &mut buffer);
+            assert_eq!(buffer, b"pdefault");
+
+            // Check that tags without namespaces does not change result
+            resolver.push(&BytesStart::borrowed(b"", 0), &mut buffer);
+            assert_eq!(buffer, b"pdefault");
+            resolver.pop(&mut buffer);
+
+            assert_eq!(buffer, b"pdefault");
+            assert_eq!(
+                resolver.resolve(name, &buffer, true),
+                (Bound(ns), LocalName(b"with-declared-prefix"))
+            );
+            assert_eq!(
+                resolver.resolve(name, &buffer, false),
+                (Bound(ns), LocalName(b"with-declared-prefix"))
+            );
+            assert_eq!(resolver.find(name, &buffer), Bound(ns));
+        }
+
+        /// Test adding a second level of namespaces, which replaces the previous binding
+        #[test]
+        fn override_namespace() {
+            let name = QName(b"p:with-declared-prefix");
+            let old_ns = Namespace(b"old");
+            let new_ns = Namespace(b"new");
+
+            let mut resolver = NamespaceResolver::default();
+            let mut buffer = Vec::new();
+
+            resolver.push(&BytesStart::borrowed(b" xmlns:p='old'", 0), &mut buffer);
+            resolver.push(&BytesStart::borrowed(b" xmlns:p='new'", 0), &mut buffer);
+
+            assert_eq!(buffer, b"poldpnew");
+            assert_eq!(
+                resolver.resolve(name, &buffer, true),
+                (Bound(new_ns), LocalName(b"with-declared-prefix"))
+            );
+            assert_eq!(
+                resolver.resolve(name, &buffer, false),
+                (Bound(new_ns), LocalName(b"with-declared-prefix"))
+            );
+            assert_eq!(resolver.find(name, &buffer), Bound(new_ns));
+
+            resolver.pop(&mut buffer);
+            assert_eq!(buffer, b"pold");
+            assert_eq!(
+                resolver.resolve(name, &buffer, true),
+                (Bound(old_ns), LocalName(b"with-declared-prefix"))
+            );
+            assert_eq!(
+                resolver.resolve(name, &buffer, false),
+                (Bound(old_ns), LocalName(b"with-declared-prefix"))
+            );
+            assert_eq!(resolver.find(name, &buffer), Bound(old_ns));
+        }
+
+        /// Test adding a second level of namespaces, which reset the previous binding
+        /// to not bound state by specifying an empty namespace name.
+        ///
+        /// See <https://www.w3.org/TR/xml-names11/#scoping>
+        #[test]
+        fn reset() {
+            let name = QName(b"p:with-declared-prefix");
+            let old_ns = Namespace(b"old");
+
+            let mut resolver = NamespaceResolver::default();
+            let mut buffer = Vec::new();
+
+            resolver.push(&BytesStart::borrowed(b" xmlns:p='old'", 0), &mut buffer);
+            resolver.push(&BytesStart::borrowed(b" xmlns:p=''", 0), &mut buffer);
+
+            assert_eq!(buffer, b"poldp");
+            assert_eq!(
+                resolver.resolve(name, &buffer, true),
+                (Unknown(b"p".to_vec()), LocalName(b"with-declared-prefix"))
+            );
+            assert_eq!(
+                resolver.resolve(name, &buffer, false),
+                (Unknown(b"p".to_vec()), LocalName(b"with-declared-prefix"))
+            );
+            assert_eq!(resolver.find(name, &buffer), Unknown(b"p".to_vec()));
+
+            resolver.pop(&mut buffer);
+            assert_eq!(buffer, b"pold");
+            assert_eq!(
+                resolver.resolve(name, &buffer, true),
+                (Bound(old_ns), LocalName(b"with-declared-prefix"))
+            );
+            assert_eq!(
+                resolver.resolve(name, &buffer, false),
+                (Bound(old_ns), LocalName(b"with-declared-prefix"))
+            );
+            assert_eq!(resolver.find(name, &buffer), Bound(old_ns));
+        }
+    }
+
+    #[test]
+    fn undeclared_prefix() {
+        let name = QName(b"unknown:prefix");
+
+        let resolver = NamespaceResolver::default();
+        let buffer = Vec::new();
+
+        assert_eq!(buffer, b"");
+        assert_eq!(
+            resolver.resolve(name, &buffer, true),
+            (Unknown(b"unknown".to_vec()), LocalName(b"prefix"))
+        );
+        assert_eq!(
+            resolver.resolve(name, &buffer, false),
+            (Unknown(b"unknown".to_vec()), LocalName(b"prefix"))
+        );
+        assert_eq!(resolver.find(name, &buffer), Unknown(b"unknown".to_vec()));
+    }
+}
diff --git a/src/reader.rs b/src/reader.rs
index 271c074..7dbf1aa 100644
--- a/src/reader.rs
+++ b/src/reader.rs
@@ -9,8 +9,8 @@ use std::{fs::File, path::Path, str::from_utf8};
 use encoding_rs::{Encoding, UTF_16BE, UTF_16LE};
 
 use crate::errors::{Error, Result};
-use crate::events::attributes::Attribute;
 use crate::events::{BytesCData, BytesDecl, BytesEnd, BytesStart, BytesText, Event};
+use crate::name::{LocalName, NamespaceResolver, QName, ResolveResult};
 
 use memchr;
 
@@ -45,7 +45,7 @@ enum TagState {
 /// loop {
 ///     match reader.read_event(&mut buf) {
 ///         Ok(Event::Start(ref e)) => {
-///             match e.name() {
+///             match e.name().as_ref() {
 ///                 b"tag1" => println!("attributes values: {:?}",
 ///                                     e.attributes().map(|a| a.unwrap().value)
 ///                                     .collect::<Vec<_>>()),
@@ -102,8 +102,14 @@ pub struct Reader<R: BufRead> {
     /// Opened name start indexes into [`Self::opened_buffer`]. See documentation
     /// for that field for details
     opened_starts: Vec<usize>,
-    /// a buffer to manage namespaces
+
+    /// A buffer to manage namespaces
     ns_resolver: NamespaceResolver,
+    /// For `Empty` events keep the 'scope' of the namespace on the stack artificially. That way, the
+    /// consumer has a chance to use `resolve` in the context of the empty element. We perform the
+    /// pop as the first operation in the next `next()` call.
+    pending_pop: bool,
+
     #[cfg(feature = "encoding")]
     /// the encoding specified in the xml, defaults to utf8
     encoding: &'static Encoding,
@@ -127,7 +133,10 @@ impl<R: BufRead> Reader<R> {
             check_end_names: true,
             buf_position: 0,
             check_comments: false,
+
             ns_resolver: NamespaceResolver::default(),
+            pending_pop: false,
+
             #[cfg(feature = "encoding")]
             encoding: ::encoding_rs::UTF_8,
             #[cfg(feature = "encoding")]
@@ -535,13 +544,18 @@ impl<R: BufRead> Reader<R> {
     /// can be defined on the same element as the attribute in question.
     ///
     /// *Unqualified* event inherits the current *default namespace*.
+    ///
+    /// # Lifetimes
+    ///
+    /// - `'n`: lifetime of an element name
+    /// - `'ns`: lifetime of a namespaces buffer, where all found namespaces are stored
     #[inline]
-    pub fn event_namespace<'a, 'b, 'c>(
-        &'a self,
-        qname: &'b [u8],
-        namespace_buffer: &'c [u8],
-    ) -> (Option<&'c [u8]>, &'b [u8]) {
-        self.ns_resolver.resolve(qname, namespace_buffer, true)
+    pub fn event_namespace<'n, 'ns>(
+        &self,
+        name: QName<'n>,
+        namespace_buffer: &'ns [u8],
+    ) -> (ResolveResult<'ns>, LocalName<'n>) {
+        self.ns_resolver.resolve(name, namespace_buffer, true)
     }
 
     /// Resolves a potentially qualified **attribute name** into (namespace name, local name).
@@ -551,13 +565,18 @@ impl<R: BufRead> Reader<R> {
     /// can be defined on the same element as the attribute in question.
     ///
     /// *Unqualified* attribute names do *not* inherit the current *default namespace*.
+    ///
+    /// # Lifetimes
+    ///
+    /// - `'n`: lifetime of an attribute
+    /// - `'ns`: lifetime of a namespaces buffer, where all found namespaces are stored
     #[inline]
-    pub fn attribute_namespace<'a, 'b, 'c>(
-        &'a self,
-        qname: &'b [u8],
-        namespace_buffer: &'c [u8],
-    ) -> (Option<&'c [u8]>, &'b [u8]) {
-        self.ns_resolver.resolve(qname, namespace_buffer, false)
+    pub fn attribute_namespace<'n, 'ns>(
+        &self,
+        name: QName<'n>,
+        namespace_buffer: &'ns [u8],
+    ) -> (ResolveResult<'ns>, LocalName<'n>) {
+        self.ns_resolver.resolve(name, namespace_buffer, false)
     }
 
     /// Reads the next event and resolves its namespace (if applicable).
@@ -568,6 +587,7 @@ impl<R: BufRead> Reader<R> {
     /// use std::str::from_utf8;
     /// use quick_xml::Reader;
     /// use quick_xml::events::Event;
+    /// use quick_xml::name::ResolveResult::*;
     ///
     /// let xml = r#"<x:tag1 xmlns:x="www.xxxx" xmlns:y="www.yyyy" att1 = "test">
     ///                 <y:tag2><!--Test comment-->Test</y:tag2>
@@ -581,14 +601,20 @@ impl<R: BufRead> Reader<R> {
     /// let mut txt = Vec::new();
     /// loop {
     ///     match reader.read_namespaced_event(&mut buf, &mut ns_buf) {
-    ///         Ok((ref ns, Event::Start(ref e))) => {
+    ///         Ok((Bound(ns), Event::Start(e))) => {
     ///             count += 1;
-    ///             match (*ns, e.local_name()) {
-    ///                 (Some(b"www.xxxx"), b"tag1") => (),
-    ///                 (Some(b"www.yyyy"), b"tag2") => (),
+    ///             match (ns.as_ref(), e.local_name().as_ref()) {
+    ///                 (b"www.xxxx", b"tag1") => (),
+    ///                 (b"www.yyyy", b"tag2") => (),
     ///                 (ns, n) => panic!("Namespace and local name mismatch"),
     ///             }
-    ///             println!("Resolved namespace: {:?}", ns.and_then(|ns| from_utf8(ns).ok()));
+    ///             println!("Resolved namespace: {:?}", ns);
+    ///         }
+    ///         Ok((Unbound, Event::Start(_))) => {
+    ///             panic!("Element not in any namespace")
+    ///         },
+    ///         Ok((Unknown(p), Event::Start(_))) => {
+    ///             panic!("Undeclared namespace prefix {:?}", String::from_utf8(p))
     ///         }
     ///         Ok((_, Event::Text(e))) => {
     ///             txt.push(e.unescape_and_decode(&reader).expect("Error!"))
@@ -602,18 +628,21 @@ impl<R: BufRead> Reader<R> {
     /// println!("Found {} start events", count);
     /// println!("Text events: {:?}", txt);
     /// ```
-    pub fn read_namespaced_event<'a, 'b, 'c>(
-        &'a mut self,
+    pub fn read_namespaced_event<'b, 'ns>(
+        &mut self,
         buf: &'b mut Vec<u8>,
-        namespace_buffer: &'c mut Vec<u8>,
-    ) -> Result<(Option<&'c [u8]>, Event<'b>)> {
-        self.ns_resolver.pop(namespace_buffer);
+        namespace_buffer: &'ns mut Vec<u8>,
+    ) -> Result<(ResolveResult<'ns>, Event<'b>)> {
+        if self.pending_pop {
+            self.ns_resolver.pop(namespace_buffer);
+        }
+        self.pending_pop = false;
         match self.read_event(buf) {
-            Ok(Event::Eof) => Ok((None, Event::Eof)),
+            Ok(Event::Eof) => Ok((ResolveResult::Unbound, Event::Eof)),
             Ok(Event::Start(e)) => {
                 self.ns_resolver.push(&e, namespace_buffer);
                 Ok((
-                    self.ns_resolver.find(e.name(), &**namespace_buffer),
+                    self.ns_resolver.find(e.name(), namespace_buffer),
                     Event::Start(e),
                 ))
             }
@@ -626,22 +655,22 @@ impl<R: BufRead> Reader<R> {
                 self.ns_resolver.push(&e, namespace_buffer);
                 // notify next `read_namespaced_event()` invocation that it needs to pop this
                 // namespace scope
-                self.ns_resolver.pending_pop = true;
+                self.pending_pop = true;
                 Ok((
-                    self.ns_resolver.find(e.name(), &**namespace_buffer),
+                    self.ns_resolver.find(e.name(), namespace_buffer),
                     Event::Empty(e),
                 ))
             }
             Ok(Event::End(e)) => {
                 // notify next `read_namespaced_event()` invocation that it needs to pop this
                 // namespace scope
-                self.ns_resolver.pending_pop = true;
+                self.pending_pop = true;
                 Ok((
-                    self.ns_resolver.find(e.name(), &**namespace_buffer),
+                    self.ns_resolver.find(e.name(), namespace_buffer),
                     Event::End(e),
                 ))
             }
-            Ok(e) => Ok((None, e)),
+            Ok(e) => Ok((ResolveResult::Unbound, e)),
             Err(e) => Err(e),
         }
     }
@@ -751,13 +780,13 @@ impl<R: BufRead> Reader<R> {
         let end = end.as_ref();
         loop {
             match self.read_event(buf) {
-                Ok(Event::End(ref e)) if e.name() == end => {
+                Ok(Event::End(ref e)) if e.name().as_ref() == end => {
                     if depth == 0 {
                         return Ok(());
                     }
                     depth -= 1;
                 }
-                Ok(Event::Start(ref e)) if e.name() == end => depth += 1,
+                Ok(Event::Start(ref e)) if e.name().as_ref() == end => depth += 1,
                 Err(e) => return Err(e),
                 Ok(Event::Eof) => {
                     return Err(Error::UnexpectedEof(format!("</{:?}>", from_utf8(end))));
@@ -806,7 +835,7 @@ impl<R: BufRead> Reader<R> {
     pub fn read_text<K: AsRef<[u8]>>(&mut self, end: K, buf: &mut Vec<u8>) -> Result<String> {
         let s = match self.read_event(buf) {
             Ok(Event::Text(e)) => e.unescape_and_decode(self),
-            Ok(Event::End(ref e)) if e.name() == end.as_ref() => return Ok("".to_string()),
+            Ok(Event::End(ref e)) if e.name().as_ref() == end.as_ref() => return Ok("".to_string()),
             Err(e) => return Err(e),
             Ok(Event::Eof) => return Err(Error::UnexpectedEof("Text".to_string())),
             _ => return Err(Error::TextNotFound),
@@ -854,7 +883,7 @@ impl<R: BufRead> Reader<R> {
     ///
     /// loop {
     ///     match reader.read_event(&mut buf) {
-    ///         Ok(Event::Start(ref e)) => match e.name() {
+    ///         Ok(Event::Start(ref e)) => match e.name().as_ref() {
     ///             b"tag1" | b"tag2" => (),
     ///             tag => {
     ///                 assert_eq!(b"tag3", tag);
@@ -917,13 +946,13 @@ impl<'a> Reader<&'a [u8]> {
         let end = end.as_ref();
         loop {
             match self.read_event_unbuffered() {
-                Ok(Event::End(ref e)) if e.name() == end => {
+                Ok(Event::End(ref e)) if e.name().as_ref() == end => {
                     if depth == 0 {
                         return Ok(());
                     }
                     depth -= 1;
                 }
-                Ok(Event::Start(ref e)) if e.name() == end => depth += 1,
+                Ok(Event::Start(ref e)) if e.name().as_ref() == end => depth += 1,
                 Err(e) => return Err(e),
                 Ok(Event::Eof) => {
                     return Err(Error::UnexpectedEof(format!("</{:?}>", from_utf8(end))));
@@ -1432,214 +1461,6 @@ pub(crate) fn is_whitespace(b: u8) -> bool {
     }
 }
 
-/// An entry that contains index into the buffer with namespace bindings.
-///
-/// Defines a mapping from *[namespace prefix]* to *[namespace name]*.
-/// If prefix is empty, defines a *default namespace* binding that applies to
-/// unprefixed element names (unprefixed attribute names do not bind to any
-/// namespace and they processing is dependent on the element in which their
-/// defined).
-///
-/// [namespace prefix]: https://www.w3.org/TR/xml-names11/#dt-prefix
-/// [namespace name]: https://www.w3.org/TR/xml-names11/#dt-NSName
-#[derive(Debug, Clone)]
-struct NamespaceEntry {
-    /// Index of the namespace in the buffer
-    start: usize,
-    /// Length of the prefix
-    /// * if greater than zero, then binds this namespace to the slice
-    ///   `[start..start + prefix_len]` in the buffer.
-    /// * else defines the current default namespace.
-    prefix_len: usize,
-    /// The length of a namespace name (the URI) of this namespace declaration.
-    /// Name started just after prefix and extend for `value_len` bytes.
-    ///
-    /// The XML standard [specifies] that an empty namespace value 'removes' a namespace declaration
-    /// for the extent of its scope. For prefix declarations that's not very interesting, but it is
-    /// vital for default namespace declarations. With `xmlns=""` you can revert back to the default
-    /// behaviour of leaving unqualified element names unqualified.
-    ///
-    /// [specifies]: https://www.w3.org/TR/xml-names11/#scoping
-    value_len: usize,
-    /// Level of nesting at which this namespace was declared. The declaring element is included,
-    /// i.e., a declaration on the document root has `level = 1`.
-    /// This is used to pop the namespace when the element gets closed.
-    level: i32,
-}
-
-impl NamespaceEntry {
-    /// Gets the namespace name (the URI) slice out of namespace buffer
-    ///
-    /// Returns `None` if namespace for this prefix was explicitly removed from
-    /// scope, using `xmlns[:prefix]=""`
-    #[inline]
-    fn namespace<'b>(&self, buffer: &'b [u8]) -> Option<&'b [u8]> {
-        if self.value_len == 0 {
-            None
-        } else {
-            let start = self.start + self.prefix_len;
-            Some(&buffer[start..start + self.value_len])
-        }
-    }
-
-    /// Check if the namespace matches the potentially qualified name
-    #[inline]
-    fn is_match(&self, buffer: &[u8], qname: &[u8]) -> bool {
-        if self.prefix_len == 0 {
-            !qname.contains(&b':')
-        } else {
-            qname.get(self.prefix_len).map_or(false, |n| *n == b':')
-                && qname.starts_with(&buffer[self.start..self.start + self.prefix_len])
-        }
-    }
-}
-
-/// A namespace management buffer.
-///
-/// Holds all internal logic to push/pop namespaces with their levels.
-#[derive(Debug, Default, Clone)]
-struct NamespaceResolver {
-    /// A stack of namespace bindings to prefixes that currently in scope
-    bindings: Vec<NamespaceEntry>,
-    /// The number of open tags at the moment. We need to keep track of this to know which namespace
-    /// declarations to remove when we encounter an `End` event.
-    nesting_level: i32,
-    /// For `Empty` events keep the 'scope' of the element on the stack artificially. That way, the
-    /// consumer has a chance to use `resolve` in the context of the empty element. We perform the
-    /// pop as the first operation in the next `next()` call.
-    pending_pop: bool,
-}
-
-impl NamespaceResolver {
-    /// Finds a [namespace name] for a given qualified name of element, borrow it
-    /// from the specified buffer.
-    ///
-    /// Returns `None`, if:
-    /// - name is unqualified
-    /// - prefix not found in the current scope
-    /// - prefix was [unbound] using `xmlns:prefix=""`
-    ///
-    /// # Lifetimes
-    ///
-    /// - `'n`: lifetime of an element name
-    /// - `'b`: lifetime of a namespaces buffer, where all found namespaces are stored
-    ///
-    /// [namespace name]: https://www.w3.org/TR/xml-names11/#dt-NSName
-    /// [unbound]: https://www.w3.org/TR/xml-names11/#scoping
-    #[inline]
-    fn find<'n, 'b>(&self, element_name: &'n [u8], buffer: &'b [u8]) -> Option<&'b [u8]> {
-        self.bindings
-            .iter()
-            .rfind(|n| n.is_match(buffer, element_name))
-            .and_then(|n| n.namespace(buffer))
-    }
-
-    /// Ends a top-most scope by popping all [namespace binding], that was added by
-    /// last call to [`Self::push()`].
-    ///
-    /// [namespace binding]: https://www.w3.org/TR/xml-names11/#dt-NSDecl
-    fn pop(&mut self, buffer: &mut Vec<u8>) {
-        if !self.pending_pop {
-            return;
-        }
-        self.pending_pop = false;
-        self.nesting_level -= 1;
-        let current_level = self.nesting_level;
-        // from the back (most deeply nested scope), look for the first scope that is still valid
-        match self.bindings.iter().rposition(|n| n.level <= current_level) {
-            // none of the namespaces are valid, remove all of them
-            None => {
-                buffer.clear();
-                self.bindings.clear();
-            }
-            // drop all namespaces past the last valid namespace
-            Some(last_valid_pos) => {
-                if let Some(len) = self.bindings.get(last_valid_pos + 1).map(|n| n.start) {
-                    buffer.truncate(len);
-                    self.bindings.truncate(last_valid_pos + 1);
-                }
-            }
-        }
-    }
-
-    /// Begins a new scope and add to it all [namespace bindings] that found in
-    /// the specified start element.
-    ///
-    /// [namespace binding]: https://www.w3.org/TR/xml-names11/#dt-NSDecl
-    fn push(&mut self, start: &BytesStart, buffer: &mut Vec<u8>) {
-        self.nesting_level += 1;
-        let level = self.nesting_level;
-        // adds new namespaces for attributes starting with 'xmlns:' and for the 'xmlns'
-        // (default namespace) attribute.
-        for a in start.attributes().with_checks(false) {
-            if let Ok(Attribute { key: k, value: v }) = a {
-                if k.starts_with(b"xmlns") {
-                    match k.get(5) {
-                        None => {
-                            let start = buffer.len();
-                            buffer.extend_from_slice(&*v);
-                            self.bindings.push(NamespaceEntry {
-                                start,
-                                prefix_len: 0,
-                                value_len: v.len(),
-                                level,
-                            });
-                        }
-                        Some(&b':') => {
-                            let start = buffer.len();
-                            buffer.extend_from_slice(&k[6..]);
-                            buffer.extend_from_slice(&*v);
-                            self.bindings.push(NamespaceEntry {
-                                start,
-                                prefix_len: k.len() - 6,
-                                value_len: v.len(),
-                                level,
-                            });
-                        }
-                        _ => break,
-                    }
-                }
-            } else {
-                break;
-            }
-        }
-    }
-
-    /// Resolves a potentially qualified **attribute name** into (namespace name, local name).
-    ///
-    /// *Qualified* attribute names have the form `prefix:local-name` where the `prefix` is defined
-    /// on any containing XML element via `xmlns:prefix="the:namespace:uri"`. The namespace prefix
-    /// can be defined on the same element as the attribute in question.
-    ///
-    /// *Unqualified* attribute names do *not* inherit the current *default namespace*.
-    ///
-    /// # Lifetimes
-    ///
-    /// - `'n`: lifetime of an attribute or an element name
-    /// - `'b`: lifetime of a namespaces buffer, where all found namespaces are stored
-    #[inline]
-    fn resolve<'n, 'b>(
-        &self,
-        qname: &'n [u8],
-        buffer: &'b [u8],
-        use_default: bool,
-    ) -> (Option<&'b [u8]>, &'n [u8]) {
-        self.bindings
-            .iter()
-            .rfind(|n| n.is_match(buffer, qname))
-            .map_or((None, qname), |n| {
-                let len = n.prefix_len;
-                if len > 0 {
-                    (n.namespace(buffer), &qname[len + 1..])
-                } else if use_default {
-                    (n.namespace(buffer), qname)
-                } else {
-                    (None, qname)
-                }
-            })
-    }
-}
-
 /// Utf8 Decoder
 #[cfg(not(feature = "encoding"))]
 #[derive(Clone, Copy, Debug)]
diff --git a/src/writer.rs b/src/writer.rs
index 0ccea96..9b4f484 100644
--- a/src/writer.rs
+++ b/src/writer.rs
@@ -23,7 +23,7 @@ use std::io::Write;
 /// let mut buf = Vec::new();
 /// loop {
 ///     match reader.read_event(&mut buf) {
-///         Ok(Event::Start(ref e)) if e.name() == b"this_tag" => {
+///         Ok(Event::Start(ref e)) if e.name().as_ref() == b"this_tag" => {
 ///
 ///             // crates a new element ... alternatively we could reuse `e` by calling
 ///             // `e.into_owned()`
@@ -38,7 +38,7 @@ use std::io::Write;
 ///             // writes the event to the writer
 ///             assert!(writer.write_event(Event::Start(elem)).is_ok());
 ///         },
-///         Ok(Event::End(ref e)) if e.name() == b"this_tag" => {
+///         Ok(Event::End(ref e)) if e.name().as_ref() == b"this_tag" => {
 ///             assert!(writer.write_event(Event::End(BytesEnd::borrowed(b"my_elem"))).is_ok());
 ///         },
 ///         Ok(Event::Eof) => break,


Test patch:
diff --git a/tests/namespaces.rs b/tests/namespaces.rs
index 76548e2..4729f2c 100644
--- a/tests/namespaces.rs
+++ b/tests/namespaces.rs
@@ -1,6 +1,8 @@
 use pretty_assertions::assert_eq;
 use quick_xml::events::attributes::Attribute;
 use quick_xml::events::Event::*;
+use quick_xml::name::ResolveResult::*;
+use quick_xml::name::{Namespace, QName};
 use quick_xml::Reader;
 use std::borrow::Cow;
 
@@ -14,7 +16,7 @@ fn namespace() {
 
     // <a>
     match r.read_namespaced_event(&mut buf, &mut ns_buf) {
-        Ok((ns, Start(_))) => assert_eq!(ns, None),
+        Ok((ns, Start(_))) => assert_eq!(ns, Unbound),
         e => panic!(
             "expecting outer start element with no namespace, got {:?}",
             e
@@ -23,7 +25,7 @@ fn namespace() {
 
     // <b>
     match r.read_namespaced_event(&mut buf, &mut ns_buf) {
-        Ok((ns, Start(_))) => assert_eq!(ns, Some(b"www1".as_ref())),
+        Ok((ns, Start(_))) => assert_eq!(ns, Bound(Namespace(b"www1"))),
         e => panic!(
             "expecting inner start element with to resolve to 'www1', got {:?}",
             e
@@ -32,12 +34,12 @@ fn namespace() {
     // "in namespace!"
     match r.read_namespaced_event(&mut buf, &mut ns_buf) {
         //TODO: Check in specification, it is true that namespace should be empty?
-        Ok((ns, Text(_))) => assert_eq!(ns, None),
+        Ok((ns, Text(_))) => assert_eq!(ns, Unbound),
         e => panic!("expecting text content with no namespace, got {:?}", e),
     }
     // </b>
     match r.read_namespaced_event(&mut buf, &mut ns_buf) {
-        Ok((ns, End(_))) => assert_eq!(ns, Some(b"www1".as_ref())),
+        Ok((ns, End(_))) => assert_eq!(ns, Bound(Namespace(b"www1"))),
         e => panic!(
             "expecting inner end element with to resolve to 'www1', got {:?}",
             e
@@ -46,7 +48,7 @@ fn namespace() {
 
     // </a>
     match r.read_namespaced_event(&mut buf, &mut ns_buf) {
-        Ok((ns, End(_))) => assert_eq!(ns, None),
+        Ok((ns, End(_))) => assert_eq!(ns, Unbound),
         e => panic!("expecting outer end element with no namespace, got {:?}", e),
     }
 }
@@ -61,7 +63,7 @@ fn default_namespace() {
 
     // <a>
     match r.read_namespaced_event(&mut buf, &mut ns_buf) {
-        Ok((ns, Start(_))) => assert_eq!(ns, None),
+        Ok((ns, Start(_))) => assert_eq!(ns, Unbound),
         e => panic!(
             "expecting outer start element with no namespace, got {:?}",
             e
@@ -70,7 +72,7 @@ fn default_namespace() {
 
     // <b>
     match r.read_namespaced_event(&mut buf, &mut ns_buf) {
-        Ok((ns, Start(_))) => assert_eq!(ns, Some(b"www1".as_ref())),
+        Ok((ns, Start(_))) => assert_eq!(ns, Bound(Namespace(b"www1"))),
         e => panic!(
             "expecting inner start element with to resolve to 'www1', got {:?}",
             e
@@ -78,7 +80,7 @@ fn default_namespace() {
     }
     // </b>
     match r.read_namespaced_event(&mut buf, &mut ns_buf) {
-        Ok((ns, End(_))) => assert_eq!(ns, Some(b"www1".as_ref())),
+        Ok((ns, End(_))) => assert_eq!(ns, Bound(Namespace(b"www1"))),
         e => panic!(
             "expecting inner end element with to resolve to 'www1', got {:?}",
             e
@@ -88,7 +90,7 @@ fn default_namespace() {
     // </a> very important: a should not be in any namespace. The default namespace only applies to
     // the sub-document it is defined on.
     match r.read_namespaced_event(&mut buf, &mut ns_buf) {
-        Ok((ns, End(_))) => assert_eq!(ns, None),
+        Ok((ns, End(_))) => assert_eq!(ns, Unbound),
         e => panic!("expecting outer end element with no namespace, got {:?}", e),
     }
 }
@@ -103,7 +105,7 @@ fn default_namespace_reset() {
 
     // <a>
     match r.read_namespaced_event(&mut buf, &mut ns_buf) {
-        Ok((ns, Start(_))) => assert_eq!(ns, Some(b"www1".as_ref())),
+        Ok((ns, Start(_))) => assert_eq!(ns, Bound(Namespace(b"www1"))),
         e => panic!(
             "expecting outer start element with to resolve to 'www1', got {:?}",
             e
@@ -112,7 +114,7 @@ fn default_namespace_reset() {
 
     // <b>
     match r.read_namespaced_event(&mut buf, &mut ns_buf) {
-        Ok((ns, Start(_))) => assert_eq!(ns, None),
+        Ok((ns, Start(_))) => assert_eq!(ns, Unbound),
         e => panic!(
             "expecting inner start element with no namespace, got {:?}",
             e
@@ -120,13 +122,13 @@ fn default_namespace_reset() {
     }
     // </b>
     match r.read_namespaced_event(&mut buf, &mut ns_buf) {
-        Ok((ns, End(_))) => assert_eq!(ns, None),
+        Ok((ns, End(_))) => assert_eq!(ns, Unbound),
         e => panic!("expecting inner end element with no namespace, got {:?}", e),
     }
 
     // </a>
     match r.read_namespaced_event(&mut buf, &mut ns_buf) {
-        Ok((ns, End(_))) => assert_eq!(ns, Some(b"www1".as_ref())),
+        Ok((ns, End(_))) => assert_eq!(ns, Bound(Namespace(b"www1"))),
         e => panic!(
             "expecting outer end element with to resolve to 'www1', got {:?}",
             e
@@ -147,7 +149,7 @@ fn attributes_empty_ns() {
     let mut ns_buf = Vec::new();
 
     let e = match r.read_namespaced_event(&mut buf, &mut ns_buf) {
-        Ok((None, Empty(e))) => e,
+        Ok((Unbound, Empty(e))) => e,
         e => panic!("Expecting Empty event, got {:?}", e),
     };
 
@@ -155,19 +157,19 @@ fn attributes_empty_ns() {
         .attributes()
         .map(|ar| ar.expect("Expecting attribute parsing to succeed."))
         // we don't care about xmlns attributes for this test
-        .filter(|kv| !kv.key.starts_with(b"xmlns"))
+        .filter(|kv| kv.key.as_namespace_binding().is_none())
         .map(|Attribute { key: name, value }| {
             let (opt_ns, local_name) = r.attribute_namespace(name, &ns_buf);
-            (opt_ns, local_name, value)
+            (opt_ns, local_name.into_inner(), value)
         });
     assert_eq!(
         attrs.next(),
-        Some((None, &b"att1"[..], Cow::Borrowed(&b"a"[..])))
+        Some((Unbound, &b"att1"[..], Cow::Borrowed(&b"a"[..])))
     );
     assert_eq!(
         attrs.next(),
         Some((
-            Some(&b"urn:example:r"[..]),
+            Bound(Namespace(b"urn:example:r")),
             &b"att2"[..],
             Cow::Borrowed(&b"b"[..])
         ))
@@ -188,7 +190,7 @@ fn attributes_empty_ns_expanded() {
     let mut ns_buf = Vec::new();
     {
         let e = match r.read_namespaced_event(&mut buf, &mut ns_buf) {
-            Ok((None, Start(e))) => e,
+            Ok((Unbound, Start(e))) => e,
             e => panic!("Expecting Empty event, got {:?}", e),
         };
 
@@ -196,19 +198,19 @@ fn attributes_empty_ns_expanded() {
             .attributes()
             .map(|ar| ar.expect("Expecting attribute parsing to succeed."))
             // we don't care about xmlns attributes for this test
-            .filter(|kv| !kv.key.starts_with(b"xmlns"))
+            .filter(|kv| kv.key.as_namespace_binding().is_none())
             .map(|Attribute { key: name, value }| {
                 let (opt_ns, local_name) = r.attribute_namespace(name, &ns_buf);
-                (opt_ns, local_name, value)
+                (opt_ns, local_name.into_inner(), value)
             });
         assert_eq!(
             attrs.next(),
-            Some((None, &b"att1"[..], Cow::Borrowed(&b"a"[..])))
+            Some((Unbound, &b"att1"[..], Cow::Borrowed(&b"a"[..])))
         );
         assert_eq!(
             attrs.next(),
             Some((
-                Some(&b"urn:example:r"[..]),
+                Bound(Namespace(b"urn:example:r")),
                 &b"att2"[..],
                 Cow::Borrowed(&b"b"[..])
             ))
@@ -217,7 +219,7 @@ fn attributes_empty_ns_expanded() {
     }
 
     match r.read_namespaced_event(&mut buf, &mut ns_buf) {
-        Ok((None, End(e))) => assert_eq!(b"a", e.name()),
+        Ok((Unbound, End(e))) => assert_eq!(e.name(), QName(b"a")),
         e => panic!("Expecting End event, got {:?}", e),
     }
 }
@@ -234,9 +236,9 @@ fn default_ns_shadowing_empty() {
     // <outer xmlns='urn:example:o'>
     {
         match r.read_namespaced_event(&mut buf, &mut ns_buf) {
-            Ok((Some(ns), Start(e))) => {
-                assert_eq!(&ns[..], b"urn:example:o");
-                assert_eq!(e.name(), b"e");
+            Ok((ns, Start(e))) => {
+                assert_eq!(ns, Bound(Namespace(b"urn:example:o")));
+                assert_eq!(e.name(), QName(b"e"));
             }
             e => panic!("Expected Start event (<outer>), got {:?}", e),
         }
@@ -245,9 +247,9 @@ fn default_ns_shadowing_empty() {
     // <inner att1='a' xmlns='urn:example:i' />
     {
         let e = match r.read_namespaced_event(&mut buf, &mut ns_buf) {
-            Ok((Some(ns), Empty(e))) => {
-                assert_eq!(::std::str::from_utf8(ns).unwrap(), "urn:example:i");
-                assert_eq!(e.name(), b"e");
+            Ok((ns, Empty(e))) => {
+                assert_eq!(ns, Bound(Namespace(b"urn:example:i")));
+                assert_eq!(e.name(), QName(b"e"));
                 e
             }
             e => panic!("Expecting Empty event, got {:?}", e),
@@ -257,25 +259,25 @@ fn default_ns_shadowing_empty() {
             .attributes()
             .map(|ar| ar.expect("Expecting attribute parsing to succeed."))
             // we don't care about xmlns attributes for this test
-            .filter(|kv| !kv.key.starts_with(b"xmlns"))
+            .filter(|kv| kv.key.as_namespace_binding().is_none())
             .map(|Attribute { key: name, value }| {
                 let (opt_ns, local_name) = r.attribute_namespace(name, &ns_buf);
-                (opt_ns, local_name, value)
+                (opt_ns, local_name.into_inner(), value)
             });
         // the attribute should _not_ have a namespace name. The default namespace does not
         // apply to attributes.
         assert_eq!(
             attrs.next(),
-            Some((None, &b"att1"[..], Cow::Borrowed(&b"a"[..])))
+            Some((Unbound, &b"att1"[..], Cow::Borrowed(&b"a"[..])))
         );
         assert_eq!(attrs.next(), None);
     }
 
     // </outer>
     match r.read_namespaced_event(&mut buf, &mut ns_buf) {
-        Ok((Some(ns), End(e))) => {
-            assert_eq!(&ns[..], b"urn:example:o");
-            assert_eq!(e.name(), b"e");
+        Ok((ns, End(e))) => {
+            assert_eq!(ns, Bound(Namespace(b"urn:example:o")));
+            assert_eq!(e.name(), QName(b"e"));
         }
         e => panic!("Expected End event (<outer>), got {:?}", e),
     }
@@ -293,9 +295,9 @@ fn default_ns_shadowing_expanded() {
     // <outer xmlns='urn:example:o'>
     {
         match r.read_namespaced_event(&mut buf, &mut ns_buf) {
-            Ok((Some(ns), Start(e))) => {
-                assert_eq!(&ns[..], b"urn:example:o");
-                assert_eq!(e.name(), b"e");
+            Ok((ns, Start(e))) => {
+                assert_eq!(ns, Bound(Namespace(b"urn:example:o")));
+                assert_eq!(e.name(), QName(b"e"));
             }
             e => panic!("Expected Start event (<outer>), got {:?}", e),
         }
@@ -305,9 +307,9 @@ fn default_ns_shadowing_expanded() {
     // <inner att1='a' xmlns='urn:example:i' />
     {
         let e = match r.read_namespaced_event(&mut buf, &mut ns_buf) {
-            Ok((Some(ns), Start(e))) => {
-                assert_eq!(&ns[..], b"urn:example:i");
-                assert_eq!(e.name(), b"e");
+            Ok((ns, Start(e))) => {
+                assert_eq!(ns, Bound(Namespace(b"urn:example:i")));
+                assert_eq!(e.name(), QName(b"e"));
                 e
             }
             e => panic!("Expecting Start event (<inner>), got {:?}", e),
@@ -316,34 +318,60 @@ fn default_ns_shadowing_expanded() {
             .attributes()
             .map(|ar| ar.expect("Expecting attribute parsing to succeed."))
             // we don't care about xmlns attributes for this test
-            .filter(|kv| !kv.key.starts_with(b"xmlns"))
+            .filter(|kv| kv.key.as_namespace_binding().is_none())
             .map(|Attribute { key: name, value }| {
                 let (opt_ns, local_name) = r.attribute_namespace(name, &ns_buf);
-                (opt_ns, local_name, value)
+                (opt_ns, local_name.into_inner(), value)
             });
         // the attribute should _not_ have a namespace name. The default namespace does not
         // apply to attributes.
         assert_eq!(
             attrs.next(),
-            Some((None, &b"att1"[..], Cow::Borrowed(&b"a"[..])))
+            Some((Unbound, &b"att1"[..], Cow::Borrowed(&b"a"[..])))
         );
         assert_eq!(attrs.next(), None);
     }
 
     // virtual </inner>
     match r.read_namespaced_event(&mut buf, &mut ns_buf) {
-        Ok((Some(ns), End(e))) => {
-            assert_eq!(&ns[..], b"urn:example:i");
-            assert_eq!(e.name(), b"e");
+        Ok((ns, End(e))) => {
+            assert_eq!(ns, Bound(Namespace(b"urn:example:i")));
+            assert_eq!(e.name(), QName(b"e"));
         }
         e => panic!("Expected End event (</inner>), got {:?}", e),
     }
     // </outer>
     match r.read_namespaced_event(&mut buf, &mut ns_buf) {
-        Ok((Some(ns), End(e))) => {
-            assert_eq!(&ns[..], b"urn:example:o");
-            assert_eq!(e.name(), b"e");
+        Ok((ns, End(e))) => {
+            assert_eq!(ns, Bound(Namespace(b"urn:example:o")));
+            assert_eq!(e.name(), QName(b"e"));
         }
         e => panic!("Expected End event (</outer>), got {:?}", e),
     }
 }
+
+/// Although the XML specification [recommends against] the use of names where
+/// the local name portion begins with the letters "xml" (case insensitive),
+/// it also specifies, that processors *MUST NOT* treat them as fatal errors.
+/// That means, that processing should continue -- in our case we should read
+/// an XML event and user should be able to check constraints later if he/she wish.
+///
+/// [recommends against]: https://www.w3.org/TR/xml-names11/#xmlReserved
+#[test]
+fn reserved_name() {
+    // Name "xmlns-something" is reserved according to spec, because started with "xml"
+    let mut r = Reader::from_str(r#"<a xmlns-something="reserved attribute name" xmlns="www1"/>"#);
+    r.trim_text(true);
+
+    let mut buf = Vec::new();
+    let mut ns_buf = Vec::new();
+
+    // <a />
+    match r.read_namespaced_event(&mut buf, &mut ns_buf) {
+        Ok((ns, Empty(_))) => assert_eq!(ns, Bound(Namespace(b"www1"))),
+        e => panic!(
+            "Expected empty element bound to namespace 'www1', got {:?}",
+            e
+        ),
+    }
+}
diff --git a/tests/test.rs b/tests/test.rs
index 36d2827..3742639 100644
--- a/tests/test.rs
+++ b/tests/test.rs
@@ -1,3 +1,4 @@
+use quick_xml::name::QName;
 use quick_xml::{events::attributes::Attribute, events::Event::*, Error, Reader};
 use std::{borrow::Cow, io::Cursor};
 
@@ -36,14 +37,14 @@ fn test_attributes_empty() {
             assert_eq!(
                 attrs.next(),
                 Some(Ok(Attribute {
-                    key: b"att1",
+                    key: QName(b"att1"),
                     value: Cow::Borrowed(b"a"),
                 }))
             );
             assert_eq!(
                 attrs.next(),
                 Some(Ok(Attribute {
-                    key: b"att2",
+                    key: QName(b"att2"),
                     value: Cow::Borrowed(b"b"),
                 }))
             );
@@ -65,7 +66,7 @@ fn test_attribute_equal() {
             assert_eq!(
                 attrs.next(),
                 Some(Ok(Attribute {
-                    key: b"att1",
+                    key: QName(b"att1"),
                     value: Cow::Borrowed(b"a=b"),
                 }))
             );
@@ -922,7 +923,7 @@ fn test_issue299() -> Result<(), Error> {
     loop {
         match reader.read_event_unbuffered()? {
             Start(e) | Empty(e) => {
-                let attr_count = match e.name() {
+                let attr_count = match e.name().as_ref() {
                     b"MICEX_DOC" => 1,
                     b"SECURITY" => 4,
                     b"RECORDS" => 26,
@@ -932,7 +933,7 @@ fn test_issue299() -> Result<(), Error> {
                     attr_count,
                     e.attributes().filter(Result::is_ok).count(),
                     "mismatch att count on '{:?}'",
-                    reader.decoder().decode(e.name())
+                    reader.decoder().decode(e.name().as_ref())
                 );
             }
             Eof => break,
diff --git a/tests/unit_tests.rs b/tests/unit_tests.rs
index 2278da5..8d82595 100644
--- a/tests/unit_tests.rs
+++ b/tests/unit_tests.rs
@@ -4,6 +4,7 @@ use std::str::from_utf8;
 
 use quick_xml::events::attributes::{AttrError, Attribute};
 use quick_xml::events::{BytesDecl, BytesEnd, BytesStart, BytesText, Event};
+use quick_xml::name::QName;
 use quick_xml::{events::Event::*, Reader, Result, Writer};
 
 use pretty_assertions::assert_eq;
@@ -12,7 +13,7 @@ macro_rules! next_eq_name {
     ($r:expr, $t:tt, $bytes:expr) => {
         let mut buf = Vec::new();
         match $r.read_event(&mut buf).unwrap() {
-            $t(ref e) if e.name() == $bytes => (),
+            $t(ref e) if e.name().as_ref() == $bytes => (),
             e => panic!(
                 "expecting {}({:?}), found {:?}",
                 stringify!($t),
@@ -678,14 +679,14 @@ fn test_closing_bracket_in_single_quote_attr() {
             assert_eq!(
                 attrs.next(),
                 Some(Ok(Attribute {
-                    key: b"attr",
+                    key: QName(b"attr"),
                     value: Cow::Borrowed(b">"),
                 }))
             );
             assert_eq!(
                 attrs.next(),
                 Some(Ok(Attribute {
-                    key: b"check",
+                    key: QName(b"check"),
                     value: Cow::Borrowed(b"2"),
                 }))
             );
@@ -707,14 +708,14 @@ fn test_closing_bracket_in_double_quote_attr() {
             assert_eq!(
                 attrs.next(),
                 Some(Ok(Attribute {
-                    key: b"attr",
+                    key: QName(b"attr"),
                     value: Cow::Borrowed(b">"),
                 }))
             );
             assert_eq!(
                 attrs.next(),
                 Some(Ok(Attribute {
-                    key: b"check",
+                    key: QName(b"check"),
                     value: Cow::Borrowed(b"2"),
                 }))
             );
@@ -736,14 +737,14 @@ fn test_closing_bracket_in_double_quote_mixed() {
             assert_eq!(
                 attrs.next(),
                 Some(Ok(Attribute {
-                    key: b"attr",
+                    key: QName(b"attr"),
                     value: Cow::Borrowed(b"'>'"),
                 }))
             );
             assert_eq!(
                 attrs.next(),
                 Some(Ok(Attribute {
-                    key: b"check",
+                    key: QName(b"check"),
                     value: Cow::Borrowed(b"'2'"),
                 }))
             );
@@ -765,14 +766,14 @@ fn test_closing_bracket_in_single_quote_mixed() {
             assert_eq!(
                 attrs.next(),
                 Some(Ok(Attribute {
-                    key: b"attr",
+                    key: QName(b"attr"),
                     value: Cow::Borrowed(br#"">""#),
                 }))
             );
             assert_eq!(
                 attrs.next(),
                 Some(Ok(Attribute {
-                    key: b"check",
+                    key: QName(b"check"),
                     value: Cow::Borrowed(br#""2""#),
                 }))
             );
diff --git a/tests/xmlrs_reader_tests.rs b/tests/xmlrs_reader_tests.rs
index 175de08..ac97416 100644
--- a/tests/xmlrs_reader_tests.rs
+++ b/tests/xmlrs_reader_tests.rs
@@ -1,4 +1,5 @@
 use quick_xml::events::{BytesStart, Event};
+use quick_xml::name::ResolveResult;
 use quick_xml::{Reader, Result};
 use std::borrow::Cow;
 use std::str::from_utf8;
@@ -387,7 +388,7 @@ fn test_bytes(input: &[u8], output: &[u8], is_short: bool) {
     loop {
         buf.clear();
         let event = reader.read_namespaced_event(&mut buf, &mut ns_buffer);
-        let line = xmlrs_display(&event, &reader);
+        let line = xmlrs_display(event, &reader);
         if let Some((n, spec)) = spec_lines.next() {
             if spec.trim() == "EndDocument" {
                 break;
@@ -426,10 +427,15 @@ fn test_bytes(input: &[u8], output: &[u8], is_short: bool) {
     }
 }
 
-fn namespace_name(n: &Option<&[u8]>, name: &[u8]) -> String {
-    match *n {
-        Some(n) => format!("{{{}}}{}", from_utf8(n).unwrap(), from_utf8(name).unwrap()),
-        None => from_utf8(name).unwrap().to_owned(),
+fn namespace_name(n: ResolveResult, name: &[u8]) -> String {
+    match n {
+        // Produces string '{namespace}local_name'
+        ResolveResult::Bound(n) => format!(
+            "{{{}}}{}",
+            from_utf8(n.as_ref()).unwrap(),
+            from_utf8(name).unwrap()
+        ),
+        _ => from_utf8(name).unwrap().to_owned(),
     }
 }
 
@@ -438,10 +444,10 @@ fn make_attrs(e: &BytesStart) -> ::std::result::Result<String, String> {
     for a in e.attributes() {
         match a {
             Ok(a) => {
-                if a.key.len() < 5 || !a.key.starts_with(b"xmlns") {
+                if a.key.as_namespace_binding().is_none() {
                     atts.push(format!(
                         "{}=\"{}\"",
-                        from_utf8(a.key).unwrap(),
+                        from_utf8(a.key.as_ref()).unwrap(),
                         from_utf8(&*a.unescaped_value().unwrap()).unwrap()
                     ));
                 }
@@ -463,26 +469,26 @@ fn decode<'a>(text: &'a [u8], reader: &Reader<&[u8]>) -> Cow<'a, str> {
     decoded
 }
 
-fn xmlrs_display(opt_event: &Result<(Option<&[u8]>, Event)>, reader: &Reader<&[u8]>) -> String {
+fn xmlrs_display(opt_event: Result<(ResolveResult, Event)>, reader: &Reader<&[u8]>) -> String {
     match opt_event {
-        Ok((ref n, Event::Start(ref e))) => {
-            let name = namespace_name(n, decode(e.name(), reader).as_bytes());
+        Ok((n, Event::Start(ref e))) => {
+            let name = namespace_name(n, decode(e.name().as_ref(), reader).as_bytes());
             match make_attrs(e) {
                 Ok(ref attrs) if attrs.is_empty() => format!("StartElement({})", &name),
                 Ok(ref attrs) => format!("StartElement({} [{}])", &name, &attrs),
                 Err(e) => format!("StartElement({}, attr-error: {})", &name, &e),
             }
         }
-        Ok((ref n, Event::Empty(ref e))) => {
-            let name = namespace_name(n, decode(e.name(), reader).as_bytes());
+        Ok((n, Event::Empty(ref e))) => {
+            let name = namespace_name(n, decode(e.name().as_ref(), reader).as_bytes());
             match make_attrs(e) {
                 Ok(ref attrs) if attrs.is_empty() => format!("EmptyElement({})", &name),
                 Ok(ref attrs) => format!("EmptyElement({} [{}])", &name, &attrs),
                 Err(e) => format!("EmptyElement({}, attr-error: {})", &name, &e),
             }
         }
-        Ok((ref n, Event::End(ref e))) => {
-            let name = namespace_name(n, decode(e.name(), reader).as_bytes());
+        Ok((n, Event::End(ref e))) => {
+            let name = namespace_name(n, decode(e.name().as_ref(), reader).as_bytes());
             format!("EndElement({})", name)
         }
         Ok((_, Event::Comment(ref e))) => format!("Comment({})", from_utf8(e).unwrap()),