Skip to content

Commit bbe26b2

Browse files
authored
float inf nan (#64)
* serialize non-finite numbers as null * they are not valid JSON numbers * this behavior matches serde-json * de: serialize JSON null as f32/f64 NAN * this is not what serde_json does (it errors instead) * but it makes ser/de somewhat symmetric * fix a clippy lint * add tests, changelog entries
1 parent 986dcab commit bbe26b2

File tree

3 files changed

+62
-18
lines changed

3 files changed

+62
-18
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
77

88
## [Unreleased]
99

10+
### Changed
11+
12+
- Changed serialization of `f32`/`f64` that are `!is_finite()` (i.e. `NAN`, `INFINITY`,
13+
`NEG_INFINITY`) to result in JSON `null`. This matches `serde_json` behavior.
14+
- Changed deserialization of JSON `null` where `f32`/`f64` is expected to result in
15+
the respective `NAN`.
16+
1017
## [v0.4.0] - 2021-05-08
1118

1219
### Added

src/de/mod.rs

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -311,23 +311,32 @@ macro_rules! deserialize_signed {
311311

312312
macro_rules! deserialize_fromstr {
313313
($self:ident, $visitor:ident, $typ:ident, $visit_fn:ident, $pattern:expr) => {{
314-
let start = $self.index;
315-
while $self.peek().is_some() {
316-
let c = $self.peek().unwrap();
317-
if $pattern.iter().find(|&&d| d == c).is_some() {
314+
match $self.parse_whitespace().ok_or(Error::EofWhileParsingValue)? {
315+
b'n' => {
318316
$self.eat_char();
319-
} else {
320-
break;
317+
$self.parse_ident(b"ull")?;
318+
$visitor.$visit_fn($typ::NAN)
321319
}
322-
}
320+
_ => {
321+
let start = $self.index;
322+
while $self.peek().is_some() {
323+
let c = $self.peek().unwrap();
324+
if $pattern.iter().find(|&&d| d == c).is_some() {
325+
$self.eat_char();
326+
} else {
327+
break;
328+
}
329+
}
323330

324-
// Note(unsafe): We already checked that it only contains ascii. This is only true if the
325-
// caller has guaranteed that `pattern` contains only ascii characters.
326-
let s = unsafe { str::from_utf8_unchecked(&$self.slice[start..$self.index]) };
331+
// Note(unsafe): We already checked that it only contains ascii. This is only true if the
332+
// caller has guaranteed that `pattern` contains only ascii characters.
333+
let s = unsafe { str::from_utf8_unchecked(&$self.slice[start..$self.index]) };
327334

328-
let v = $typ::from_str(s).or(Err(Error::InvalidNumber))?;
335+
let v = $typ::from_str(s).or(Err(Error::InvalidNumber))?;
329336

330-
$visitor.$visit_fn(v)
337+
$visitor.$visit_fn(v)
338+
}
339+
}
331340
}};
332341
}
333342

@@ -423,15 +432,13 @@ impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> {
423432
where
424433
V: Visitor<'de>,
425434
{
426-
self.parse_whitespace().ok_or(Error::EofWhileParsingValue)?;
427435
deserialize_fromstr!(self, visitor, f32, visit_f32, b"0123456789+-.eE")
428436
}
429437

430438
fn deserialize_f64<V>(self, visitor: V) -> Result<V::Value>
431439
where
432440
V: Visitor<'de>,
433441
{
434-
self.parse_whitespace().ok_or(Error::EofWhileParsingValue)?;
435442
deserialize_fromstr!(self, visitor, f64, visit_f64, b"0123456789+-.eE")
436443
}
437444

@@ -941,11 +948,17 @@ mod tests {
941948
))
942949
);
943950

951+
// NaNs will always compare unequal.
952+
let (r, n): (Temperature, usize) = crate::from_str(r#"{ "temperature": null }"#).unwrap();
953+
assert!(r.temperature.is_nan());
954+
assert_eq!(n, 23);
955+
944956
assert!(crate::from_str::<Temperature>(r#"{ "temperature": 1e1e1 }"#).is_err());
945957
assert!(crate::from_str::<Temperature>(r#"{ "temperature": -2-2 }"#).is_err());
946958
assert!(crate::from_str::<Temperature>(r#"{ "temperature": 1 1 }"#).is_err());
947959
assert!(crate::from_str::<Temperature>(r#"{ "temperature": 0.0. }"#).is_err());
948960
assert!(crate::from_str::<Temperature>(r#"{ "temperature": ä }"#).is_err());
961+
assert!(crate::from_str::<Temperature>(r#"{ "temperature": None }"#).is_err());
949962
}
950963

951964
#[test]

src/ser/mod.rs

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -230,11 +230,19 @@ impl<'a, 'b: 'a> ser::Serializer for &'a mut Serializer<'b> {
230230
}
231231

232232
fn serialize_f32(self, v: f32) -> Result<Self::Ok> {
233-
serialize_ryu!(self, v)
233+
if v.is_finite() {
234+
serialize_ryu!(self, v)
235+
} else {
236+
self.serialize_none()
237+
}
234238
}
235239

236240
fn serialize_f64(self, v: f64) -> Result<Self::Ok> {
237-
serialize_ryu!(self, v)
241+
if v.is_finite() {
242+
serialize_ryu!(self, v)
243+
} else {
244+
self.serialize_none()
245+
}
238246
}
239247

240248
fn serialize_char(self, _v: char) -> Result<Self::Ok> {
@@ -345,7 +353,7 @@ impl<'a, 'b: 'a> ser::Serializer for &'a mut Serializer<'b> {
345353
}
346354

347355
fn serialize_newtype_variant<T: ?Sized>(
348-
mut self,
356+
self,
349357
_name: &'static str,
350358
_variant_index: u32,
351359
variant: &'static str,
@@ -355,7 +363,7 @@ impl<'a, 'b: 'a> ser::Serializer for &'a mut Serializer<'b> {
355363
T: ser::Serialize,
356364
{
357365
self.push(b'{')?;
358-
let mut s = SerializeStruct::new(&mut self);
366+
let mut s = SerializeStruct::new(self);
359367
s.serialize_field(variant, value)?;
360368
s.end()?;
361369
Ok(())
@@ -700,6 +708,22 @@ mod tests {
700708
.unwrap(),
701709
r#"{"temperature":-2.3456788e-23}"#
702710
);
711+
712+
assert_eq!(
713+
&*crate::to_string::<_, N>(&Temperature {
714+
temperature: f32::NAN
715+
})
716+
.unwrap(),
717+
r#"{"temperature":null}"#
718+
);
719+
720+
assert_eq!(
721+
&*crate::to_string::<_, N>(&Temperature {
722+
temperature: f32::NEG_INFINITY
723+
})
724+
.unwrap(),
725+
r#"{"temperature":null}"#
726+
);
703727
}
704728

705729
#[test]

0 commit comments

Comments
 (0)