diff --git a/src/number.rs b/src/number.rs index 3ba0846..4bb7882 100644 --- a/src/number.rs +++ b/src/number.rs @@ -202,6 +202,29 @@ impl Number { self.n.parse().ok() } + /// If the `Number` is an integer, represent it as i128 if possible. Returns + /// None otherwise. + /// + /// ``` + /// # use serde_json::json; + /// # + /// let v = json!({ "a": 64, "b": 256.0 }); + /// + /// assert_eq!(v["a"].as_i128(), Some(64)); + /// assert_eq!(v["b"].as_i128(), None); + /// ``` + #[inline] + pub fn as_i128(&self) -> Option { + #[cfg(not(feature = "arbitrary_precision"))] + match self.n { + N::PosInt(n) => Some(n as i128), + N::NegInt(n) => Some(n as i128), + N::Float(_) => None, + } + #[cfg(feature = "arbitrary_precision")] + self.n.parse().ok() + } + /// If the `Number` is an integer, represent it as u64 if possible. Returns /// None otherwise. /// @@ -279,6 +302,65 @@ impl Number { } } + /// Converts an `i128` to a `Number`. Greater than u64::MAX values are not JSON + /// numbers. + /// + /// ``` + /// # use std::i128; + /// # + /// # use serde_json::Number; + /// # + /// assert!(Number::from_i128(256).is_some()); + /// ``` + #[inline] + pub fn from_i128(i: i128) -> Option { + match u64::try_from(i) { + Ok(u) => { + let n = { + #[cfg(not(feature = "arbitrary_precision"))] + { + N::PosInt(u) + } + #[cfg(feature = "arbitrary_precision")] + { + u.to_string() + } + }; + Some(Number{ n }) + }, + Err(_) => match i64::try_from(i) { + Ok(i) => { + if i >= 0 { + let n = { + #[cfg(not(feature = "arbitrary_precision"))] + { + N::PosInt(i as u64) + } + #[cfg(feature = "arbitrary_precision")] + { + i.to_string() + } + }; + Some(Number{ n }) + } else { + let n = { + #[cfg(not(feature = "arbitrary_precision"))] + { + N::NegInt(i) + } + #[cfg(feature = "arbitrary_precision")] + { + i.to_string() + } + }; + Some(Number{ n }) + } + }, + Err(_) => None + } + } + } + /// Returns the exact original JSON representation that this Number was /// parsed from. /// @@ -414,6 +496,13 @@ impl<'de> Deserialize<'de> for Number { Ok(value.into()) } + fn visit_i128(self, value: i128) -> Result + where + E: de::Error, + { + Number::from_i128(value).ok_or_else(|| de::Error::custom("not a JSON number")) + } + #[inline] fn visit_u64(self, value: u64) -> Result { Ok(value.into()) @@ -543,6 +632,8 @@ macro_rules! deserialize_any { return visitor.visit_u64(u); } else if let Some(i) = self.as_i64() { return visitor.visit_i64(i); + } else if let Some(i) = self.as_i128() { + return visitor.visit_i128(i); } else if let Some(f) = self.as_f64() { if ryu::Buffer::new().format_finite(f) == self.n || f.to_string() == self.n { return visitor.visit_f64(f); diff --git a/src/value/de.rs b/src/value/de.rs index 9367256..c38253a 100644 --- a/src/value/de.rs +++ b/src/value/de.rs @@ -44,6 +44,11 @@ impl<'de> Deserialize<'de> for Value { Ok(Value::Number(value.into())) } + #[inline] + fn visit_i128(self, value: i128) -> Result { + Ok(Number::from_i128(value).map_or(Value::Null, Value::Number)) + } + #[inline] fn visit_u64(self, value: u64) -> Result { Ok(Value::Number(value.into())) diff --git a/src/value/mod.rs b/src/value/mod.rs index 6b40f9a..704577e 100644 --- a/src/value/mod.rs +++ b/src/value/mod.rs @@ -635,6 +635,24 @@ impl Value { } } + /// If the `Value` is an integer, represent it as i128 if possible. Returns + /// None otherwise. + /// + /// ``` + /// # use serde_json::json; + /// # + /// let v = json!({ "a": 64, "b": 256.0 }); + /// + /// assert_eq!(v["a"].as_i128(), Some(64)); + /// assert_eq!(v["b"].as_i128(), None); + /// ``` + pub fn as_i128(&self) -> Option { + match self { + Value::Number(n) => n.as_i128(), + _ => None, + } + } + /// If the `Value` is an integer, represent it as u64 if possible. Returns /// None otherwise. ///