Skip to content

Commit 44644b3

Browse files
authored
Merge pull request #850 from Mingun/fix/honor_xsi_nil
Properly use xsi:nil to deserialize null values via serde
2 parents c8fe850 + 3c4de88 commit 44644b3

File tree

9 files changed

+1023
-31
lines changed

9 files changed

+1023
-31
lines changed

.github/workflows/cifuzz.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ jobs:
1717
language: rust
1818
fuzz-seconds: 600
1919
- name: Upload Crash
20-
uses: actions/upload-artifact@v3
20+
uses: actions/upload-artifact@v4
2121
if: failure() && steps.build.outcome == 'success'
2222
with:
2323
name: artifacts

Cargo.toml

+5
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,11 @@ name = "serde-de-seq"
231231
required-features = ["serialize"]
232232
path = "tests/serde-de-seq.rs"
233233

234+
[[test]]
235+
name = "serde-de-xsi"
236+
required-features = ["serialize"]
237+
path = "tests/serde-de-xsi.rs"
238+
234239
[[test]]
235240
name = "serde-se"
236241
required-features = ["serialize"]

Changelog.md

+7
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,17 @@
1515

1616
### New Features
1717

18+
- [#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`.
20+
- [#497]: Handle `xsi:nil` attribute in serde Deserializer to better process optional fields.
21+
1822
### Bug Fixes
1923

2024
### Misc Changes
2125

26+
[#497]: https://github.com/tafia/quick-xml/issues/497
27+
[#850]: https://github.com/tafia/quick-xml/pull/850
28+
2229

2330
## 0.37.2 -- 2024-12-29
2431

src/de/map.rs

+22-1
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,21 @@ where
215215
has_value_field: fields.contains(&VALUE_KEY),
216216
})
217217
}
218+
219+
/// Determines if subtree started with the specified event shoould be skipped.
220+
///
221+
/// Used to map elements with `xsi:nil` attribute set to true to `None` in optional contexts.
222+
///
223+
/// We need to handle two attributes:
224+
/// - on parent element: <map xsi:nil="true"><foo/></map>
225+
/// - on this element: <map><foo xsi:nil="true"/></map>
226+
///
227+
/// We check parent element too because `xsi:nil` affects only nested elements of the
228+
/// tag where it is defined. We can map structure with fields mapped to attributes to
229+
/// the `<map>` element and set to `None` all its optional elements.
230+
fn should_skip_subtree(&self, start: &BytesStart) -> bool {
231+
self.de.reader.reader.has_nil_attr(&self.start) || self.de.reader.reader.has_nil_attr(start)
232+
}
218233
}
219234

220235
impl<'de, 'd, R, E> MapAccess<'de> for ElementMapAccess<'de, 'd, R, E>
@@ -540,8 +555,14 @@ where
540555
where
541556
V: Visitor<'de>,
542557
{
543-
match self.map.de.peek()? {
558+
// We cannot use result of `peek()` directly because of borrow checker
559+
let _ = self.map.de.peek()?;
560+
match self.map.de.last_peeked() {
544561
DeEvent::Text(t) if t.is_empty() => visitor.visit_none(),
562+
DeEvent::Start(start) if self.map.should_skip_subtree(start) => {
563+
self.map.de.skip_next_tree()?;
564+
visitor.visit_none()
565+
}
545566
_ => visitor.visit_some(self),
546567
}
547568
}

0 commit comments

Comments
 (0)