From 80b5479ae3aaa17be13f4a21bcf29d627a63db38 Mon Sep 17 00:00:00 2001 From: Georg Semmler Date: Tue, 30 Jun 2026 16:10:07 +0200 Subject: [PATCH] Fix incorrect decreasing of nesting level in NamespaceResolver This commit fixes an incorrect decreasing of the nesting level in the NamespaceResolver through the ns_reader. We could end up there in a state where we request a future decreasing of the nesting level without pushing an actual increase. This could cause namespaces to be unregistered too early. Fix #953 --- src/reader/ns_reader.rs | 6 +++++- tests/serde-de-xsi.rs | 45 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/src/reader/ns_reader.rs b/src/reader/ns_reader.rs index e0c89d646..672e33ae1 100644 --- a/src/reader/ns_reader.rs +++ b/src/reader/ns_reader.rs @@ -10,7 +10,7 @@ use std::ops::Deref; use std::path::Path; use crate::errors::Result; -use crate::events::{BytesText, Event}; +use crate::events::{BytesStart, BytesText, Event}; use crate::name::{NamespaceResolver, QName, ResolveResult}; use crate::reader::{Config, Reader, Span, XmlSource}; @@ -91,6 +91,10 @@ impl NsReader { Ok(Event::Empty(e)) } Ok(Event::End(e)) => { + // push a fake element to the `ns_resolver` as the pending pop will remove it again + // if we don't push this element we don't increase the level, but the subsequent pending pop will + // decrease it anyway. + self.ns_resolver.push(&BytesStart::new(""))?; // notify next `read_event_impl()` invocation that it needs to pop this // namespace scope self.pending_pop = true; diff --git a/tests/serde-de-xsi.rs b/tests/serde-de-xsi.rs index 4db9e605f..4bbb3bf55 100644 --- a/tests/serde-de-xsi.rs +++ b/tests/serde-de-xsi.rs @@ -702,3 +702,48 @@ mod as_field { } } } + +/// Regression test for https://github.com/tafia/quick-xml/issues/953. +mod issue953 { + use super::*; + + #[derive(Debug, Deserialize)] + struct BoreholeType { + #[serde(rename = "boreholeSegment")] + borehole_segment: Vec<()>, + + #[serde(rename = "drillingProcess")] + drilling_process: Option, + } + + #[derive(Debug, Deserialize)] + struct DrillingProcess { + #[serde(rename = "DrillingProcess")] + drilling_process: (), + } + + // success before 210d3e1d460a4c63e13d455b0b15623b77fa669c + // failed since 210d3e1d460a4c63e13d455b0b15623b77fa669c + #[test] + fn open_close() { + let input = r#" + + + + + "#; + let _: BoreholeType = from_str(&input).unwrap(); + } + + // failed always + #[test] + fn self_closed() { + let input = r#" + + + + + "#; + let _: BoreholeType = from_str(&input).unwrap(); + } +}