Skip to content

Commit 341db46

Browse files
committed
Add helper method to check if xsi:nil value is set in attributes
1 parent 41b6dc1 commit 341db46

File tree

3 files changed

+79
-2
lines changed

3 files changed

+79
-2
lines changed

Changelog.md

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
### New Features
1717

1818
- [#850]: Add `Attribute::as_bool()` method to get an attribute value as a boolean.
19+
- [#850]: Add `Attributes::has_nil()` method to check if attributes has `xsi:nil` attribute set to `true`.
1920

2021
### Bug Fixes
2122

src/events/attributes.rs

+77-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
use crate::encoding::Decoder;
66
use crate::errors::Result as XmlResult;
77
use crate::escape::{escape, resolve_predefined_entity, unescape_with};
8-
use crate::name::QName;
8+
use crate::name::{LocalName, Namespace, QName};
9+
use crate::reader::NsReader;
910
use crate::utils::{is_whitespace, Bytes};
1011

1112
use std::fmt::{self, Debug, Display, Formatter};
@@ -269,6 +270,81 @@ impl<'a> Attributes<'a> {
269270
self.state.check_duplicates = val;
270271
self
271272
}
273+
274+
/// Checks if the current tag has a [`xsi:nil`] attribute. This method ignores any errors in
275+
/// attributes.
276+
///
277+
/// # Examples
278+
///
279+
/// ```
280+
/// # use pretty_assertions::assert_eq;
281+
/// use quick_xml::events::Event;
282+
/// use quick_xml::name::QName;
283+
/// use quick_xml::reader::NsReader;
284+
///
285+
/// let mut reader = NsReader::from_str("
286+
/// <root xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'>
287+
/// <true xsi:nil='true'/>
288+
/// <false xsi:nil='false'/>
289+
/// <none/>
290+
/// <non-xsi xsi:nil='true' xmlns:xsi='namespace'/>
291+
/// <unbound-nil nil='true' xmlns='http://www.w3.org/2001/XMLSchema-instance'/>
292+
/// <another-xmlns f:nil='true' xmlns:f='http://www.w3.org/2001/XMLSchema-instance'/>
293+
/// </root>
294+
/// ");
295+
/// reader.config_mut().trim_text(true);
296+
///
297+
/// macro_rules! check {
298+
/// ($reader:expr, $name:literal, $value:literal) => {
299+
/// let event = match $reader.read_event().unwrap() {
300+
/// Event::Empty(e) => e,
301+
/// e => panic!("Unexpected event {:?}", e),
302+
/// };
303+
/// assert_eq!(
304+
/// (event.name(), event.attributes().has_nil(&$reader)),
305+
/// (QName($name.as_bytes()), $value),
306+
/// );
307+
/// };
308+
/// }
309+
///
310+
/// let root = match reader.read_event().unwrap() {
311+
/// Event::Start(e) => e,
312+
/// e => panic!("Unexpected event {:?}", e),
313+
/// };
314+
/// assert_eq!(root.attributes().has_nil(&reader), false);
315+
///
316+
/// // definitely true
317+
/// check!(reader, "true", true);
318+
/// // definitely false
319+
/// check!(reader, "false", false);
320+
/// // absence of the attribute means that attribute is not set
321+
/// check!(reader, "none", false);
322+
/// // attribute not bound to the correct namespace
323+
/// check!(reader, "non-xsi", false);
324+
/// // attributes without prefix not bound to any namespace
325+
/// check!(reader, "unbound-nil", false);
326+
/// // prefix can be any while it is bound to the correct namespace
327+
/// check!(reader, "another-xmlns", true);
328+
/// ```
329+
///
330+
/// [`xsi:nil`]: https://www.w3.org/TR/xmlschema-1/#xsi_nil
331+
pub fn has_nil<R>(&mut self, reader: &NsReader<R>) -> bool {
332+
use crate::name::ResolveResult::*;
333+
334+
self.any(|attr| {
335+
if let Ok(attr) = attr {
336+
match reader.resolve_attribute(attr.key) {
337+
(
338+
Bound(Namespace(b"http://www.w3.org/2001/XMLSchema-instance")),
339+
LocalName(b"nil"),
340+
) => attr.as_bool().unwrap_or_default(),
341+
_ => false,
342+
}
343+
} else {
344+
false
345+
}
346+
})
347+
}
272348
}
273349

274350
impl<'a> Debug for Attributes<'a> {

src/name.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ impl<'a> AsRef<[u8]> for QName<'a> {
200200
/// [local (unqualified) name]: https://www.w3.org/TR/xml-names11/#dt-localname
201201
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
202202
#[cfg_attr(feature = "serde-types", derive(serde::Deserialize, serde::Serialize))]
203-
pub struct LocalName<'a>(&'a [u8]);
203+
pub struct LocalName<'a>(pub(crate) &'a [u8]);
204204
impl<'a> LocalName<'a> {
205205
/// Converts this name to an internal slice representation.
206206
#[inline(always)]

0 commit comments

Comments
 (0)