Using serde_json, I have JSON objects with String
s that I need to convert to floats. I\'ve stumbled upon a custom deserializer solution, but it seems like a hac
Using coercible
worked kind-of by accident. With it, the input "3.141"
was stripped of its ""
s, so I had 3.141
being fed into serde_json::from_str(&j)
, which appropriately returned a float. This accidental solution broke easily and confusingly when, e.g., the input JSON contained unexpected values.
I read the Serde docs (a great learning exercise) and came up with the appropriate way to convert a string to a f64
upon deserialization of JSON (working playground here):
#[macro_use]
extern crate serde_derive;
extern crate serde;
extern crate serde_json;
use std::fmt;
use serde_json::Error;
use serde::de::{self, Deserializer, Unexpected, Visitor};
#[derive(Serialize, Deserialize)]
struct Example {
#[serde(deserialize_with = "string_as_f64")]
first: f64,
second: f64,
}
fn string_as_f64<'de, D>(deserializer: D) -> Result<f64, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_f64(F64Visitor)
}
struct F64Visitor;
impl<'de> Visitor<'de> for F64Visitor {
type Value = f64;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a string representation of a f64")
}
fn visit_str<E>(self, value: &str) -> Result<f64, E>
where
E: de::Error,
{
value.parse::<f64>().map_err(|_err| {
E::invalid_value(Unexpected::Str(value), &"a string representation of a f64")
})
}
}
fn typed_example() -> Result<(), Error> {
let data = r#"["3.141",1.618]"#;
let e: Example = serde_json::from_str(data)?;
println!("{} {}", e.first * 2.0, e.second * 2.0);
Ok(())
}
fn main() {
typed_example().unwrap();
}
Kudos to the Serde devs, because although the Serde documentation seemed totally obtuse to my eyes, it actually proved to be very helpful and comprehensible. I just had to start from the top and read through slowly.