Skip to content

Commit e8ae020

Browse files
authored
Merge pull request #738 from Frankkkkk/flattened_enum_example
examples: add flattened enum deserialization
2 parents 6e34a73 + 5d76174 commit e8ae020

File tree

3 files changed

+117
-0
lines changed

3 files changed

+117
-0
lines changed

Cargo.toml

+5
Original file line numberDiff line numberDiff line change
@@ -242,3 +242,8 @@ path = "tests/async-tokio.rs"
242242
name = "read_nodes_serde"
243243
required-features = ["serialize"]
244244
path = "examples/read_nodes_serde.rs"
245+
246+
[[example]]
247+
name = "flattened_enum"
248+
required-features = ["serialize"]
249+
path = "examples/flattened_enum.rs"

Changelog.md

+3
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ to get an offset of the error position. For `SyntaxError`s the range
6060
- [#362]: Now default quote level is `QuoteLevel::Partial` when using serde serializer.
6161
- [#689]: `buffer_position()` now always report the position the parser last seen.
6262
To get an error position use `error_position()`.
63+
- [#738]: Add an example of how to deserialize XML elements into Rust enums using an
64+
intermediate custom deserializer.
6365

6466
[#362]: https://github.com/tafia/quick-xml/issues/362
6567
[#513]: https://github.com/tafia/quick-xml/issues/513
@@ -70,6 +72,7 @@ to get an offset of the error position. For `SyntaxError`s the range
7072
[#689]: https://github.com/tafia/quick-xml/pull/689
7173
[#704]: https://github.com/tafia/quick-xml/pull/704
7274
[#705]: https://github.com/tafia/quick-xml/pull/705
75+
[#738]: https://github.com/tafia/quick-xml/pull/738
7376

7477

7578
## 0.31.0 -- 2023-10-22

examples/flattened_enum.rs

+109
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
//! This example demonstrates how to deserialize enum nodes using an intermediate
2+
//! custom deserializer.
3+
//! The `elem` node can either be a `Foo` or a `Bar` node, depending on the `type`.
4+
//! The `type` attribute is used to determine which variant to deserialize.
5+
//! This is a workaround for [serde's issue](https://github.com/serde-rs/serde/issues/1905)
6+
//!
7+
//! note: to use serde, the feature needs to be enabled
8+
//! run example with:
9+
//! cargo run --example flattened_enum --features="serialize"
10+
11+
use std::fmt;
12+
13+
use quick_xml::de::from_str;
14+
use serde::de::value::MapAccessDeserializer;
15+
use serde::de::{Error, MapAccess, Visitor};
16+
use serde::Deserialize;
17+
18+
#[derive(Debug, Deserialize, PartialEq)]
19+
struct Model {
20+
elem: Vec<Elem>,
21+
}
22+
23+
#[derive(Debug, PartialEq)]
24+
enum Elem {
25+
Foo(Foo),
26+
Bar(Bar),
27+
}
28+
29+
impl<'de> Deserialize<'de> for Elem {
30+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
31+
where
32+
D: serde::Deserializer<'de>,
33+
{
34+
struct ElemVisitor;
35+
36+
impl<'de> Visitor<'de> for ElemVisitor {
37+
type Value = Elem;
38+
39+
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
40+
formatter.write_str("an object with a `type` field")
41+
}
42+
43+
fn visit_map<A>(self, mut map: A) -> Result<Elem, A::Error>
44+
where
45+
A: MapAccess<'de>,
46+
{
47+
if let Some((key, value)) = map.next_entry::<String, String>()? {
48+
return match key.as_str() {
49+
"@type" => match value.as_str() {
50+
"foo" => {
51+
let f = Foo::deserialize(MapAccessDeserializer::new(map))?;
52+
Ok(Elem::Foo(f))
53+
}
54+
"bar" => {
55+
let f = Bar::deserialize(MapAccessDeserializer::new(map))?;
56+
Ok(Elem::Bar(f))
57+
}
58+
t => Err(Error::custom(format!("unknown type attribute `{t}`"))),
59+
},
60+
a => Err(Error::custom(format!(
61+
"expected attribute `type`, but found `{a}`"
62+
))),
63+
};
64+
}
65+
Err(Error::custom("expected `type` attribute"))
66+
}
67+
}
68+
deserializer.deserialize_map(ElemVisitor)
69+
}
70+
}
71+
72+
#[derive(Debug, Deserialize, PartialEq)]
73+
struct Foo {
74+
a: String,
75+
subfoo: SubFoo,
76+
}
77+
78+
#[derive(Debug, Deserialize, PartialEq)]
79+
struct SubFoo {
80+
a1: String,
81+
a2: String,
82+
a3: String,
83+
}
84+
85+
#[derive(Debug, Deserialize, PartialEq)]
86+
struct Bar {
87+
b: String,
88+
}
89+
90+
fn main() {
91+
let x = r#"
92+
<model>
93+
<elem type="foo">
94+
<a>1</a>
95+
<subfoo>
96+
<a1>2</a1>
97+
<a2>42</a2>
98+
<a3>1337</a3>
99+
</subfoo>
100+
</elem>
101+
<elem type="bar">
102+
<b>22</b>
103+
</elem>
104+
</model>
105+
"#;
106+
107+
let model: Model = from_str(&x).unwrap();
108+
println!("{:?}", model);
109+
}

0 commit comments

Comments
 (0)