Skip to content

Commit

Permalink
examples: add flattened enum deserialization
Browse files Browse the repository at this point in the history
Add a small example about flattened enum deserialization. This is a
small workaround around serde's [serde's
issue](serde-rs/serde#1905).

Big thanks to @Mingun for (most) of the code + help!

Signed-off-by: Frank Villaro-Dixon <frank@villaro-dixon.eu>
  • Loading branch information
Frankkkkk committed Apr 20, 2024
1 parent 6e34a73 commit e74f294
Show file tree
Hide file tree
Showing 2 changed files with 120 additions and 0 deletions.
5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -242,3 +242,8 @@ path = "tests/async-tokio.rs"
name = "read_nodes_serde"
required-features = ["serialize"]
path = "examples/read_nodes_serde.rs"

[[example]]
name = "flattened_enum"
required-features = ["serialize"]
path = "examples/flattened_enum.rs"
115 changes: 115 additions & 0 deletions examples/flattened_enum.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
//! This example demonstrates how to deserialize enum nodes using an intermediate
//! custom deserializer.
//! The `elem` node can either be a `Foo` or a `Bar` node, depending on the `type`.
//! The `type` attribute is used to determine which variant to deserialize.
//! This is a workaround for [serde's issue](https://github.com/serde-rs/serde/issues/1905)
//!
//! note: to use serde, the feature needs to be enabled
//! run example with:
//! cargo run --example flattened_enum --features="serialize"

use std::fmt;

use serde::Deserialize;
use quick_xml::de::from_str;
use serde::{
de::{self, value::MapAccessDeserializer, MapAccess, Visitor},
Deserialize,
};

#[derive(Debug, Deserialize, PartialEq)]
struct Model {
elem: Vec<Elem>,
}

#[derive(Debug, PartialEq)]
enum Elem {
Foo(Foo),
Bar(Bar),
}

impl<'de> Deserialize<'de> for Elem {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
struct ElemVisitor;

impl<'de> Visitor<'de> for ElemVisitor {
type Value = Elem;

fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("an object with a `type` field")
}

fn visit_map<A>(self, mut map: A) -> Result<Elem, A::Error>
where
A: MapAccess<'de>,
{
//use crate::_::_serde::de::Error;
if let Some((key, value)) = map.next_entry::<String, String>()? {
return match key.as_str() {
"@type" => match value.as_str() {
"foo" => {
let f = Foo::deserialize(MapAccessDeserializer::new(map))?;
Ok(Elem::Foo(f))
}
"bar" => {
let f = Bar::deserialize(MapAccessDeserializer::new(map))?;
Ok(Elem::Bar(f))
}
//t => Err(A::Error::custom(format!("unknown type attribute `{t}`"))),
//t => {panic!("{}", format!("unknown type attribute `{}`", t))},
t => Err(de::Error::custom(format!("unknown type attribute `{t}`"))),
},
a => Err(de::Error::custom(format!(
"expected attribute `type`, but found `{a}`"
))),
};
}
//Err(DeError::Custom("expected `type` attribute"))
panic!("expected `type` attribute")
}
}
deserializer.deserialize_map(ElemVisitor)
}
}

#[derive(Debug, Deserialize, PartialEq)]
struct Foo {
a: String,
subfoo: SubFoo,
}

#[derive(Debug, Deserialize, PartialEq)]
struct SubFoo {
a1: String,
a2: String,
a3: String,
}

#[derive(Debug, Deserialize, PartialEq)]
struct Bar {
b: String,
}

fn main() {
let x = r#"
<model>
<elem type="foo">
<a>1</a>
<subfoo>
<a1>2</a1>
<a2>42</a2>
<a3>1337</a3>
</subfoo>
</elem>
<elem type="bar">
<b>22</b>
</elem>
</model>
"#;

let model: Model = from_str(&x).unwrap();
println!("{:?}", model);
}

0 comments on commit e74f294

Please sign in to comment.