I'm trying to use quick_xml derives to deserialize some data that has elements with attributes that can be: optional, empty or fully defined elements.
I've tried to use the following code to deal with those, in a generic way (I could define as optional all attributes in the complex case and check if required fields are defined or not but this is too ad-hoc.
Do you have any clues?
I have the following code, but the test_full case is not working (it always matches EmptyOrValue::Empty and never EmptyOrValue::Value):
use serde::{de, Deserialize, Serialize};
/// Deserialize optional value, considering empty as None
///
/// Use:
///
/// pub struct Container {
/// #[serde(deserialize_with="empty_struct_to_none")]
/// pub inner: Option<Inner>
/// }
///
/// pub struct Inner { pub attr: u32 }
///
/// <Container></Container> -> Container { inner: None }
/// <Container><inner></inner></Container> -> Container { inner: None }
/// <Container><inner/></Container> -> Container { inner: None }
/// <Container><inner><attr>8</attr></inner></Container> -> Container { inner: Some(Inner{ attr: 8 }) }
pub fn empty_struct_to_none<'de, D, T>(deserializer: D) -> Result<Option<T>, D::Error>
where
D: de::Deserializer<'de>,
T: de::Deserialize<'de> + std::fmt::Debug,
{
#[derive(Serialize, Deserialize, Debug)]
#[serde(untagged)]
enum EmptyOrValue<T> {
Value(T),
Empty(()),
}
#[derive(Serialize, Deserialize, Default, Debug)]
pub struct Wrapper<T> {
#[serde(flatten)]
pub content: Option<EmptyOrValue<T>>,
}
let w: Wrapper<T> = de::Deserialize::deserialize(deserializer)?;
// println!("{:#?}", w);
match w.content {
None => Ok(None),
Some(EmptyOrValue::Empty(())) => Ok(None),
Some(EmptyOrValue::Value(v)) => Ok(Some(v)),
}
}
#[derive(PartialEq, Debug, Serialize, Deserialize)]
pub struct Container {
#[serde(
default,
deserialize_with = "empty_struct_to_none",
skip_serializing_if = "Option::is_none"
)]
pub inner: Option<Inner>,
}
#[derive(PartialEq, Debug, Serialize, Deserialize)]
pub struct Inner {
pub attr: i32,
}
#[test]
fn test_missing() -> Result<(), quick_xml::DeError> {
assert_eq!(
quick_xml::de::from_str::<Container>("<Container></Container>")?,
Container { inner: None }
);
Ok(())
}
#[test]
fn test_empty() -> Result<(), quick_xml::DeError> {
assert_eq!(
quick_xml::de::from_str::<Container>("<Container><inner></inner></Container>")?,
Container { inner: None }
);
Ok(())
}
#[test]
fn test_empty2() -> Result<(), quick_xml::DeError> {
assert_eq!(
quick_xml::de::from_str::<Container>("<Container><inner/></Container>")?,
Container { inner: None }
);
Ok(())
}
#[test]
fn test_full() -> Result<(), quick_xml::DeError> {
assert_eq!(
quick_xml::de::from_str::<Container>(
"<Container><inner><attr>8</attr></inner></Container>"
)?,
Container {
inner: Some(Inner { attr: 8 })
}
);
Ok(())
}
I'm using as dependencies:
[dependencies]
serde = { version = "1.0", features = ["derive"] }
quick-xml = { version = "0.37", features = ["serialize"] }