python_json_read_adapter/
lib.rs

1//! This crate implements a `Read` adapter that converts the invalid JSON
2//! tokens `NaN` and `Infinity` into other tokens without otherwise distorting
3//! the stream.  It achieves this by converting `NaN` and `Infinity` into `0.0`.
4//! 
5//! This is useful because the Python JSON library traditionally emits invalid
6//! JSON if `NaN` and `Infinity` values are encountered.  If you have to support
7//! clients like this, this wrapper can be used to still deserialize such a
8//! JSON document.
9//! 
10//! This is just a way to get this to parse and `0.0` is the only value that can
11//! be inserted in a standardized way that fits without changing any of the
12//! positions.
13//! 
14//! # Example Conversion
15//! 
16//! The following JSON document:
17//! 
18//! ```ignore
19//! {"nan":NaN,"inf":Infinity,"-inf":-Infinity}
20//! ```
21//! 
22//! is thus converted to:
23//! 
24//! ```ignore
25//! {"nan":0.0,"inf":0.0     ,"-inf":-0.0     }
26//! ```
27//! 
28//! # serde support
29//! 
30//! If the `serde` feature is enabled then the crate provides some basic
31//! wrappers around `serde_json` to deserialize quickly and also by running
32//! the conversions.
33use std::io::{self, Read};
34use std::fmt;
35
36#[cfg(feature = "serde")]
37mod serde_impl;
38#[cfg(feature = "serde")]
39pub use self::serde_impl::*;
40
41#[derive(Copy, Clone)]
42enum State {
43    Initial,
44    Quoted,
45    QuotedEscape,
46    NaN0,
47    NaN1,
48    Infinity0,
49    Infinity1,
50    Infinity2,
51    Infinity3,
52    Infinity4,
53    Infinity5,
54    Infinity6,
55}
56
57/// A reader that transparently translates python JSON compat tokens.
58pub struct JsonCompatRead<R> {
59    reader: R,
60    state: State,
61}
62
63impl<R: Read> fmt::Debug for JsonCompatRead<R> {
64    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
65        f.debug_struct("JsonCompatRead").finish()
66    }
67}
68
69impl<R: Read> JsonCompatRead<R> {
70    /// Wraps another reader.
71    pub fn wrap(reader: R) -> JsonCompatRead<R> {
72        JsonCompatRead { reader, state: State::Initial }
73    }
74}
75
76impl<R: Read> Read for JsonCompatRead<R> {
77    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
78        let read = io::Read::read(&mut self.reader, buf)?;
79        self.state = translate_slice_impl(&mut buf[..read], self.state);
80        Ok(read)
81    }
82}
83
84fn translate_slice_impl(bytes: &mut [u8], mut state: State) -> State {
85    for c in bytes {
86        let rv = match (state, *c) {
87            (State::Initial, b'N') => (State::NaN0, b'0'),
88            (State::NaN0, b'a') => (State::NaN1, b'.'),
89            (State::NaN1, b'N') => (State::Initial, b'0'),
90            (State::Initial, b'I') => (State::Infinity0, b'0'),
91            (State::Infinity0, b'n') => (State::Infinity1, b'.'),
92            (State::Infinity1, b'f') => (State::Infinity2, b'0'),
93            (State::Infinity2, b'i') => (State::Infinity3, b' '),
94            (State::Infinity3, b'n') => (State::Infinity4, b' '),
95            (State::Infinity4, b'i') => (State::Infinity5, b' '),
96            (State::Infinity5, b't') => (State::Infinity6, b' '),
97            (State::Infinity6, b'y') => (State::Initial, b' '),
98            (State::Initial, b'"') => (State::Quoted, b'"'),
99            (State::Quoted, b'\\') => (State::QuotedEscape, b'\\'),
100            (State::QuotedEscape, c) => (State::Quoted, c),
101            (State::Quoted, b'"') => (State::Initial, b'"'),
102            (state, c) => (state, c),
103        };
104        state = rv.0;
105        *c = rv.1;
106    }
107    state
108}
109
110/// Translates a slice in place.
111/// 
112/// This works the same as the `JsonCompatRead` struct but instead converts a
113/// slice in place.  This is useful when working with JSON in slices.
114pub fn translate_slice(bytes: &mut [u8]) {
115    translate_slice_impl(bytes, State::Initial);
116}
117
118#[test]
119fn test_reader_simple() {
120    let json = r#"{"nan":0.0,"inf":Infinity,"-inf":-Infinity}"#;
121    assert_eq!(json.len(), 43);
122    let mut rdr = JsonCompatRead::wrap(json.as_bytes());
123    let mut rv = String::new();
124    let read = rdr.read_to_string(&mut rv).unwrap();
125    assert_eq!(read, 43);
126    assert_eq!(rv, "{\"nan\":0.0,\"inf\":0.0     ,\"-inf\":-0.0     }");
127}
128
129#[test]
130fn test_reader_string() {
131    let json = r#"{"nan":"nan","Infinity":"-Infinity","other":NaN}"#;
132    assert_eq!(json.len(), 48);
133    let mut rdr = JsonCompatRead::wrap(json.as_bytes());
134    let mut rv = String::new();
135    let read = rdr.read_to_string(&mut rv).unwrap();
136    assert_eq!(read, 48);
137    assert_eq!(rv, "{\"nan\":\"nan\",\"Infinity\":\"-Infinity\",\"other\":0.0}");
138}
139
140#[test]
141fn test_reader_string_escaping() {
142    let json = r#""NaN\"NaN\"NaN""#;
143    assert_eq!(json.len(), 15);
144    let mut rdr = JsonCompatRead::wrap(json.as_bytes());
145    let mut rv = String::new();
146    let read = rdr.read_to_string(&mut rv).unwrap();
147    assert_eq!(read, 15);
148    assert_eq!(rv, r#""NaN\"NaN\"NaN""#);
149}
150
151#[test]
152fn test_translate_slice() {
153    let mut json = br#"{"nan":"nan","Infinity":"-Infinity","other":NaN}"#.to_vec();
154    translate_slice(&mut json[..]);
155    assert_eq!(&json[..], &b"{\"nan\":\"nan\",\"Infinity\":\"-Infinity\",\"other\":0.0}"[..]);
156}