Skip to content

Instantly share code, notes, and snippets.

@gaganjakhotiya
Last active September 6, 2018 17:24
Show Gist options
  • Save gaganjakhotiya/063653aeafb4c80540b7adc314531661 to your computer and use it in GitHub Desktop.
Save gaganjakhotiya/063653aeafb4c80540b7adc314531661 to your computer and use it in GitHub Desktop.
Serde Custom Deserializer
#[macro_use]
extern crate serde_json;
#[macro_use]
extern crate serde_derive;
extern crate serde;
use serde::{
de::{Error, MapAccess, Unexpected, Visitor},
Deserialize, Deserializer,
};
use std::fmt;
#[derive(Clone, Debug, Serialize)]
struct Galaxy {
name: Option<String>,
total_stars: Option<i32>,
}
impl<'de> Deserialize<'de> for Galaxy {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct GalaxyVisitor;
impl<'de> Visitor<'de> for GalaxyVisitor {
type Value = Galaxy;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("valid Galaxy")
}
fn visit_map<D>(self, mut map: D) -> Result<Self::Value, D::Error>
where
D: MapAccess<'de>,
{
let mut galaxy = Galaxy {
name: None,
total_stars: None,
};
while let Some((key, value)) = map.next_entry::<String, serde_json::Value>()? {
match &key[..] {
"name" => {
galaxy.name = Some(
serde_json::from_value::<String>(value.clone()).map_err(|_| {
D::Error::invalid_type(
Unexpected::Other(format!("{:?}", value).as_ref()),
&self,
)
})?,
);
Ok(())?
}
"total_stars" => {
galaxy.total_stars = Some(match serde_json::from_value::<i32>(
value.clone(),
) {
Ok(val) => Ok(val),
_ => match serde_json::from_value::<String>(value.clone()) {
Ok(val) => val.parse::<i32>().map_err(|_| {
D::Error::invalid_type(Unexpected::Str(val.as_ref()), &self)
}),
_ => Err(D::Error::invalid_value(
Unexpected::Other(format!("{:?}", value).as_ref()),
&self,
)),
},
}?);
Ok(())?
}
_ => {}
}
}
Ok(galaxy)
}
}
deserializer.deserialize_struct("Galaxy", &["name", "total_stars"], GalaxyVisitor)
}
}
fn main() {
println!(
"{:?}\n{:?}\n{:?}\n{:?}\n{:?}\n{:?}",
serde_json::from_value::<Galaxy>(json!({ "total_stars": "3131231", "name": "Backyard" })), // Ok
serde_json::from_value::<Galaxy>(json!({ "total_stars": 989031213, "name": "Rooftop" })), // Ok
serde_json::from_value::<Galaxy>(json!({ "name": "Unknown" })), // Ok
serde_json::from_value::<Galaxy>(json!({ "total_stars": 0 })), // Ok
serde_json::from_value::<Galaxy>(json!({ "total_stars": "aa" })), // Err
serde_json::from_value::<Galaxy>(json!({ "name": 231 })), // Err
)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment