use serde::{Deserialize, Deserializer, Serializer};
use serde_json::Value;
fn number_from(value: &Value) -> Option<f64> {
match value {
Value::Number(n) => n.as_f64(),
Value::String(s) => s.trim().parse::<f64>().ok().filter(|f| f.is_finite()),
Value::Bool(b) => Some(if *b { 1.0 } else { 0.0 }),
_ => None,
}
}
pub(crate) fn serialize_f64<S: Serializer>(v: f64, s: S) -> Result<S::Ok, S::Error> {
if v.fract() == 0.0 && v.abs() < 9.0e15 {
s.serialize_i64(v as i64)
} else {
s.serialize_f64(v)
}
}
pub(crate) mod num {
use super::{Deserialize, Deserializer, Serializer, Value, number_from, serialize_f64};
pub(crate) fn serialize<S: Serializer>(v: &Option<f64>, s: S) -> Result<S::Ok, S::Error> {
match v {
Some(v) => serialize_f64(*v, s),
None => s.serialize_none(),
}
}
pub(crate) fn deserialize<'de, D: Deserializer<'de>>(d: D) -> Result<Option<f64>, D::Error> {
Ok(Option::<Value>::deserialize(d)?
.as_ref()
.and_then(number_from))
}
}
pub(crate) mod num_or_zero {
use super::{Deserializer, Serializer, serialize_f64};
pub(crate) fn serialize<S: Serializer>(v: &f64, s: S) -> Result<S::Ok, S::Error> {
serialize_f64(*v, s)
}
pub(crate) fn deserialize<'de, D: Deserializer<'de>>(d: D) -> Result<f64, D::Error> {
Ok(super::num::deserialize(d)?.unwrap_or_default())
}
}
pub(crate) mod int {
use super::{Deserialize, Deserializer, Serializer, Value, number_from};
pub(crate) fn serialize<S: Serializer>(v: &Option<i64>, s: S) -> Result<S::Ok, S::Error> {
match v {
Some(v) => s.serialize_i64(*v),
None => s.serialize_none(),
}
}
pub(crate) fn deserialize<'de, D: Deserializer<'de>>(d: D) -> Result<Option<i64>, D::Error> {
Ok(Option::<Value>::deserialize(d)?.and_then(|v| match &v {
Value::Number(n) => n.as_i64().or_else(|| n.as_f64().map(|f| f.round() as i64)),
other => number_from(other).map(|f| f.round() as i64),
}))
}
}
pub(crate) mod int32 {
use super::{Deserializer, Serializer};
pub(crate) fn serialize<S: Serializer>(v: &Option<i32>, s: S) -> Result<S::Ok, S::Error> {
match v {
Some(v) => s.serialize_i32(*v),
None => s.serialize_none(),
}
}
pub(crate) fn deserialize<'de, D: Deserializer<'de>>(d: D) -> Result<Option<i32>, D::Error> {
Ok(super::int::deserialize(d)?.and_then(|v| i32::try_from(v).ok()))
}
}
pub(crate) mod flag {
use super::{Deserialize, Deserializer, Serializer, Value};
pub(crate) fn serialize<S: Serializer>(v: &Option<bool>, s: S) -> Result<S::Ok, S::Error> {
match v {
Some(v) => s.serialize_bool(*v),
None => s.serialize_none(),
}
}
pub(crate) fn deserialize<'de, D: Deserializer<'de>>(d: D) -> Result<Option<bool>, D::Error> {
Ok(Option::<Value>::deserialize(d)?.and_then(|v| match v {
Value::Bool(b) => Some(b),
Value::Number(n) => n.as_f64().map(|f| f != 0.0),
Value::String(s) => match s.trim().to_ascii_lowercase().as_str() {
"true" | "1" | "yes" => Some(true),
"false" | "0" | "no" => Some(false),
_ => None,
},
_ => None,
}))
}
}
pub(crate) mod text {
use super::{Deserialize, Deserializer, Serializer, Value};
pub(crate) fn serialize<S: Serializer>(v: &Option<String>, s: S) -> Result<S::Ok, S::Error> {
match v {
Some(v) => s.serialize_str(v),
None => s.serialize_none(),
}
}
pub(crate) fn deserialize<'de, D: Deserializer<'de>>(d: D) -> Result<Option<String>, D::Error> {
Ok(Option::<Value>::deserialize(d)?.and_then(|v| match v {
Value::String(s) => Some(s),
Value::Number(n) => Some(n.to_string()),
Value::Bool(b) => Some(b.to_string()),
_ => None,
}))
}
}
pub(crate) mod nums {
use super::{Deserialize, Deserializer, Serializer, Value, number_from, serialize_f64};
use serde::ser::SerializeSeq;
pub(crate) fn serialize<S: Serializer>(v: &[f64], s: S) -> Result<S::Ok, S::Error> {
struct Num(f64);
impl serde::Serialize for Num {
fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
serialize_f64(self.0, s)
}
}
let mut seq = s.serialize_seq(Some(v.len()))?;
for x in v {
seq.serialize_element(&Num(*x))?;
}
seq.end()
}
pub(crate) fn deserialize<'de, D: Deserializer<'de>>(d: D) -> Result<Vec<f64>, D::Error> {
Ok(Option::<Vec<Value>>::deserialize(d)?
.unwrap_or_default()
.iter()
.filter_map(number_from)
.collect())
}
}
pub(crate) fn null_as_empty<'de, D, T>(d: D) -> Result<Vec<T>, D::Error>
where
D: Deserializer<'de>,
T: Deserialize<'de>,
{
Ok(Option::<Vec<T>>::deserialize(d)?.unwrap_or_default())
}
pub(crate) fn tolerant<'de, D, T>(d: D) -> Result<Option<T>, D::Error>
where
D: Deserializer<'de>,
T: serde::de::DeserializeOwned,
{
Ok(Option::<Value>::deserialize(d)?.and_then(|v| serde_json::from_value(v).ok()))
}
#[cfg(test)]
mod tests {
use serde::{Deserialize, Serialize};
#[derive(Deserialize, Serialize)]
struct Probe {
#[serde(default, skip_serializing_if = "Option::is_none", with = "super::num")]
f: Option<f64>,
#[serde(default, skip_serializing_if = "Option::is_none", with = "super::int")]
i: Option<i64>,
#[serde(default, skip_serializing_if = "Option::is_none", with = "super::flag")]
b: Option<bool>,
}
fn probe(json: &str) -> Probe {
serde_json::from_str(json).unwrap_or_else(|e| panic!("{json}: {e}"))
}
#[test]
fn accepts_what_uploaders_send() {
assert_eq!(probe(r#"{"f":"1.5"}"#).f, Some(1.5));
assert_eq!(probe(r#"{"f":""}"#).f, None);
assert_eq!(probe(r#"{"f":null}"#).f, None);
assert_eq!(probe(r"{}").f, None);
assert_eq!(probe(r#"{"i":"171"}"#).i, Some(171));
assert_eq!(probe(r#"{"i":170.6}"#).i, Some(171));
assert_eq!(probe(r#"{"b":"false"}"#).b, Some(false));
assert_eq!(probe(r#"{"b":1}"#).b, Some(true));
assert_eq!(probe(r#"{"f":"NaN"}"#).f, None);
}
#[test]
fn whole_floats_serialize_as_integers() {
let json = serde_json::to_string(&probe(r#"{"f":131000}"#)).unwrap_or_default();
assert_eq!(json, r#"{"f":131000}"#);
let json = serde_json::to_string(&probe(r#"{"f":2.5}"#)).unwrap_or_default();
assert_eq!(json, r#"{"f":2.5}"#);
}
}