You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
And this main.rs (run it with cargo test -- --nocapture)
use serde::{Deserialize,Deserializer};use std::marker::PhantomData;use std::str::FromStr;use void::Void;use serde::de::{self,Visitor,MapAccess};use std::fmt;// #[serde(deserialize_with = "string_or_struct")]#[derive(Debug,Deserialize)]pubstructArtist{pubname:String,pubmbid:Option<String>,puburl:Option<String>,publisteners:Option<usize>,pubstreamable:Option<bool>,}implFromStrforArtist{// This implementation of `from_str` can never fail, so use the impossible// `Void` type as the error type.typeErr = Void;fnfrom_str(s:&str) -> Result<Self,Self::Err>{Ok(Artist{name: s.to_string(),mbid:None,url:None,listeners:None,streamable:None,})}}fnstring_or_struct<'de,T,D>(deserializer:D) -> Result<T,D::Error>whereT:Deserialize<'de> + FromStr<Err = Void>,D:Deserializer<'de>,{// This is a Visitor that forwards string types to T's `FromStr` impl and// forwards map types to T's `Deserialize` impl. The `PhantomData` is to// keep the compiler from complaining about T being an unused generic type// parameter. We need T in order to know the Value type for the Visitor// impl.structStringOrStruct<T>(PhantomData<fn() -> T>);impl<'de,T>Visitor<'de>forStringOrStruct<T>whereT:Deserialize<'de> + FromStr<Err = Void>,{typeValue = T;fnexpecting(&self,formatter:&mut fmt::Formatter) -> fmt::Result{
formatter.write_str("string or map")}fnvisit_str<E>(self,value:&str) -> Result<T,E>whereE: de::Error,{println!("visit_string ({value})");Ok(FromStr::from_str(value).unwrap())}fnvisit_map<M>(self,map:M) -> Result<T,M::Error>whereM:MapAccess<'de>,{// `MapAccessDeserializer` is a wrapper that turns a `MapAccess`// into a `Deserializer`, allowing it to be used as the input to T's// `Deserialize` implementation. T then deserializes itself using// the entries from the map visitor.Deserialize::deserialize(de::value::MapAccessDeserializer::new(map))}}
deserializer.deserialize_any(StringOrStruct(PhantomData))}#[derive(Deserialize,Debug)]pubstructAlbum{pubname:String,#[serde(deserialize_with = "string_or_struct")]pubartist:Artist,}typeAlbums = Vec<Album>;#[derive(Deserialize,Debug)]pubstructSearch{pubstatus:String,pubalbum:Albums,}#[cfg(test)]mod tests {usesuper::*;#[test]fntest_json_deserialization(){let src =
r#"{ "status":"ok", "album": [ { "name": "Youthanasia", "artist": { "name":"Megadeth", "mbid": "a9044915-8be3-4c7e-b11f-9e2d2ea0a91e", "url": "https://www.last.fm/music/Megadeth2" } }, { "name": "Dystopia", "artist": "Megadeth" } ]}"#;match serde_json::from_str::<Search>(src){Ok(nfo) => {println!("Search: {:#?}", nfo)},Err(e)=> {println!("Error: {}", e);assert!(false)}}}// this fails#[test]fntest_xml_deserialization(){let src =
r#"<?xml version="1.0" encoding="UTF-8" ?><lfm status="ok"> <album> <name>Youthanasia</name> <artist> <name>Megadeth</name> <mbid>a9044915-8be3-4c7e-b11f-9e2d2ea0a91e</mbid> <url>https://www.last.fm/music/Megadeth</url> </artist> </album> <album> <name>Youthanasia</name> <artist>Metallica</artist> </album> /lfm>"#;match serde_xml_rs::from_str::<Search>(src){Ok(nfo) => {println!("Search: {:#?}", nfo)},Err(e)=> {println!("Error: {}", e);assert!(false)}}}}fnmain(){println!("Dummy main")}
The text was updated successfully, but these errors were encountered:
@diegoefe Did you find a resolution to this issue? I'm attempting to de-serialize Windows Event Log XML which has exactly this issue (string vs struct)
The customization works using serde-json but not the serde-xml-rs' one.
The example is based on this doc page: Deserialize either a string or a struct
Using this Cargo.toml's dependencies:
And this main.rs (run it with cargo test -- --nocapture)
The text was updated successfully, but these errors were encountered: