Skip to content

Conversation

@Mingun
Copy link
Collaborator

@Mingun Mingun commented Aug 10, 2025

This introduces small breaking change -- Attributes::has_nil now accepts &NamespaceResolver instead of Reader<R> and does not need to be generic anymore (which is good).

Other changes that may look as breaking in the diff not breaking changes, because they was made on initially private types.

@Mingun Mingun added namespaces Issues related to namespaces support enhancement labels Aug 10, 2025
@Mingun
Copy link
Collaborator Author

Mingun commented Aug 10, 2025

@faassen want to do a review?

@codecov-commenter
Copy link

codecov-commenter commented Aug 10, 2025

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

❌ Patch coverage is 67.69231% with 42 lines in your changes missing coverage. Please review.
✅ Project coverage is 58.17%. Comparing base (38b44d4) to head (b12ef9f).
⚠️ Report is 84 commits behind head on master.

Files with missing lines Patch % Lines
src/name.rs 67.24% 38 Missing ⚠️
src/reader/ns_reader.rs 63.63% 4 Missing ⚠️
❗ Your organization needs to install the Codecov GitHub app to enable full functionality.
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #893      +/-   ##
==========================================
+ Coverage   55.52%   58.17%   +2.64%     
==========================================
  Files          42       42              
  Lines       15511    15231     -280     
==========================================
+ Hits         8613     8860     +247     
+ Misses       6898     6371     -527     
Flag Coverage Δ
unittests 58.17% <67.69%> (+2.64%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@Mingun Mingun force-pushed the public-ns-resolver branch from c1bf733 to 73c66f8 Compare August 10, 2025 19:14
Copy link

@faassen faassen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the main blocker for my use cases is a way to push my own namespace bindings without having to construct a BytesStart.

I wanted to mention this use case in addition:

In my codebase I have both OwnedPrefixDeclaration and OwnedNamespace. Their primary use case is to be able to create and store this kind of stuff in configuration for the various operations I need to do over them. So, I need to be able to create them independently from events. Perhaps I overlooked a way to do owned versions of PrefixDeclaration and Namespace themselves.

I'll also note that my OwnedPrefixDeclaration stores the whole xmlns:foo bit, rather than just the prefix like PrefixDeclaration does, because otherwise I need to do allocation each time I want to turn it into a QName, which I want to do when I want to construct an namespace declaration XML attribute from it. Note that this doing this without allocation is a use case not currently supported by quick_xml's PrefixDeclaration either, afaik.

self.any(|attr| {
if let Ok(attr) = attr {
match reader.resolve_attribute(attr.key) {
match resolver.resolve(attr.key, true) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be nice if the resolver gained resolve_element and resolve_attribute as convenience APIs too, mirroring what's on BytesStart.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, will add

///
/// [namespace binding]: https://www.w3.org/TR/xml-names11/#dt-NSDecl
/// [namespace bindings]: https://www.w3.org/TR/xml-names11/#dt-NSDecl
pub fn push(&mut self, start: &BytesStart) -> Result<(), NamespaceError> {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In c14n I have a use case where I want to filter through the actually utilized namespace declarations and selectively add them on a namespace resolver I manage (and also use the one for the NsReader).

So I'm looking for an API where I can add PrefixDeclaration directly.

I expected a new for NamespaceResolver resolver but there is only a Default. Should we add a new too that calls default? In addition to that I'd like to be able to pass some "top-level" bindings in when I construct the resolver, but I think doing a push immediately after construction should be sufficient to do this, as its nesting level is independent from that of the document anyway as far as I can tell.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So you would like to have this lines extracted into the new add(PrefixDeclaration, Namespace) method?

quick-xml/src/name.rs

Lines 547 to 591 in 73c66f8

match k.as_namespace_binding() {
Some(PrefixDeclaration::Default) => {
let start = self.buffer.len();
self.buffer.extend_from_slice(&v);
self.bindings.push(NamespaceBinding {
start,
prefix_len: 0,
value_len: v.len(),
level,
});
}
Some(PrefixDeclaration::Named(b"xml")) => {
if Namespace(&v) != RESERVED_NAMESPACE_XML.1 {
// error, `xml` prefix explicitly set to different value
return Err(NamespaceError::InvalidXmlPrefixBind(v.to_vec()));
}
// don't add another NamespaceEntry for the `xml` namespace prefix
}
Some(PrefixDeclaration::Named(b"xmlns")) => {
// error, `xmlns` prefix explicitly set
return Err(NamespaceError::InvalidXmlnsPrefixBind(v.to_vec()));
}
Some(PrefixDeclaration::Named(prefix)) => {
let ns = Namespace(&v);
if ns == RESERVED_NAMESPACE_XML.1 {
// error, non-`xml` prefix set to xml uri
return Err(NamespaceError::InvalidPrefixForXml(prefix.to_vec()));
} else if ns == RESERVED_NAMESPACE_XMLNS.1 {
// error, non-`xmlns` prefix set to xmlns uri
return Err(NamespaceError::InvalidPrefixForXmlns(prefix.to_vec()));
}
let start = self.buffer.len();
self.buffer.extend_from_slice(prefix);
self.buffer.extend_from_slice(&v);
self.bindings.push(NamespaceBinding {
start,
prefix_len: prefix.len(),
value_len: v.len(),
level,
});
}
None => {}
}

I doesn't add new because there is already default, but calling that requires that Default trait was in scope. So maybe the new method has the right to life. Actually, I would like to see it constant (which is impossible for trait method implementations yet).

/// [`Empty`]: Event::Empty
/// [`Start`]: Event::Start
/// [`End`]: Event::End
pub fn resolve_event<'i>(&self, event: Event<'i>) -> (ResolveResult<'_>, Event<'i>) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One operation I use is this:

is_declared (given a prefix declaration and namespace, return a boolean if it's declared. this also needs to understand undeclarations, I think quick_xml supports namespaces 1.1 so I think anything could be undeclared?)

I can build this myself but I'm mentioning it in case we want to add this.

Copy link
Collaborator Author

@Mingun Mingun Aug 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably .bindings().any() is enough for this?

I think quick_xml supports namespaces 1.1 so I think anything could be undeclared?

Yes, we support 1.1, but I also want to support 1.0 rules, because when XML declaration defined 1.0 document, we 1.1 parser should apply 1.0 rules.

xmlns and xml prefixes cannot be undeclared, it is an error to do that (that is follow from forbidding of they redeclaration with another namespace)

use Event::*;

match event {
Empty(e) => (self.resolve_prefix(e.name().prefix(), true), Empty(e)),
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a minor comment, but initially I responded with, but I need resolve_prefix in the public API! But now I realize it's already exposed indirectly through resolve. Perhaps this code can use resolve rather than resolve_prefix, or we could even merge the two into one - always use the "attribute" bool rather than introduce a use_default concept where the boolean needs to be flipped. (even though the use_default concept is fine by itself).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I used the attribute parameter, because that is what using by the NsReader methods, and I want to be consistent here. But if make resolve_prefix public, we can return to the use_default conception

#[inline]
pub fn resolve_element<'n>(&self, name: QName<'n>) -> (ResolveResult<'_>, LocalName<'n>) {
self.ns_resolver.resolve(name, true)
self.ns_resolver.resolve(name, false)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The flipping of true to false in this PR in quite a few places is basically because we're not using a single resolve_prefix API with a consistent meaning for the boolean.

@Mingun Mingun force-pushed the public-ns-resolver branch from 73c66f8 to 63e192b Compare August 17, 2025 12:34
@Mingun Mingun force-pushed the public-ns-resolver branch from 63e192b to b12ef9f Compare August 19, 2025 16:20
@Mingun Mingun merged commit b6b9ead into tafia:master Aug 19, 2025
7 checks passed
@Mingun Mingun deleted the public-ns-resolver branch August 19, 2025 16:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement namespaces Issues related to namespaces support

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants