Skip to content

Commit

Permalink
Fix number deserialization with arbitrary_precision
Browse files Browse the repository at this point in the history
When we have a struct that uses `#[serde(flatten)]` and the feature
`arbitrary_precision` is enabled, the deserialization. The problem was
that the arbitrary number was forwarded as a "map" to the visitor and in
a later step this map failed as a number was expected.

Fixes: #505
  • Loading branch information
bkchr committed Nov 21, 2019
1 parent 7dda823 commit 9d01c09
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 4 deletions.
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -66,3 +66,6 @@ raw_value = []
# overflow the stack after deserialization has completed, including, but not
# limited to, Display and Debug and Drop impls.
unbounded_depth = []

[patch.crates-io]
serde = { git = "https://github.com/bkchr/serde.git", branch = "bkchr-tagged-content-u128-i128" }
38 changes: 35 additions & 3 deletions src/de.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@ use read::{self, Reference};
pub use read::{IoRead, Read, SliceRead, StrRead};

use number::Number;
#[cfg(feature = "arbitrary_precision")]
use number::NumberDeserializer;

//////////////////////////////////////////////////////////////////////////////

Expand Down Expand Up @@ -107,6 +105,40 @@ pub enum ParserNumber {
String(String),
}

#[cfg(feature = "arbitrary_precision")]
impl ParserNumber {
fn parse_arbitrary_precision<'de, V>(number: &str, visitor: V) -> Result<V::Value>
where
V: de::Visitor<'de>,
{
let err = || Err(number::invalid_number());

serde_if_integer128! {
let res = if let Some(val) = number.parse::<i128>().ok() {
visitor.visit_i128(val)
} else if let Some(val) = number.parse::<u128>().ok() {
visitor.visit_u128(val)
} else if let Some(val) = number.parse::<f64>().ok() {
visitor.visit_f64(val)
} else {
err()
};

return res;
}

if let Some(val) = number.parse::<i64>().ok() {
visitor.visit_i64(val)
} else if let Some(val) = number.parse::<u64>().ok() {
visitor.visit_u64(val)
} else if let Some(val) = number.parse::<f64>().ok() {
visitor.visit_f64(val)
} else {
err()
}
}
}

impl ParserNumber {
fn visit<'de, V>(self, visitor: V) -> Result<V::Value>
where
Expand All @@ -117,7 +149,7 @@ impl ParserNumber {
ParserNumber::U64(x) => visitor.visit_u64(x),
ParserNumber::I64(x) => visitor.visit_i64(x),
#[cfg(feature = "arbitrary_precision")]
ParserNumber::String(x) => visitor.visit_map(NumberDeserializer { number: x.into() }),
ParserNumber::String(x) => Self::parse_arbitrary_precision(&x, visitor),
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/number.rs
Original file line number Diff line number Diff line change
Expand Up @@ -444,7 +444,7 @@ impl<'de> de::Deserialize<'de> for NumberFromString {
}

#[cfg(feature = "arbitrary_precision")]
fn invalid_number() -> Error {
pub(crate) fn invalid_number() -> Error {
Error::syntax(ErrorCode::InvalidNumber, 0, 0)
}

Expand Down
22 changes: 22 additions & 0 deletions tests/regression/issue505.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
use serde_derive::Deserialize;

#[derive(Deserialize)]
struct Data {
_value: i32,
_value2: i128,
}

#[derive(Deserialize)]
#[serde(tag = "type")]
enum Wrapper {
Data(Data),
}

#[test]
fn test() {
let json = r#"{"type":"Data","_value":123,"_value2":123244235436463}"#;
// Okay
let _data1: Data = serde_json::from_str(json).unwrap();
// Fails!
let _data2: Wrapper = serde_json::from_str(json).unwrap();
}

0 comments on commit 9d01c09

Please sign in to comment.