serde_json/
error.rs

1//! When serializing or deserializing JSON goes wrong.
2
3use std::error;
4use std::fmt::{self, Debug, Display};
5use std::io;
6use std::result;
7use std::str::FromStr;
8
9use serde::de;
10use serde::ser;
11
12/// This type represents all possible errors that can occur when serializing or
13/// deserializing JSON data.
14pub struct Error {
15    /// This `Box` allows us to keep the size of `Error` as small as possible. A
16    /// larger `Error` type was substantially slower due to all the functions
17    /// that pass around `Result<T, Error>`.
18    err: Box<ErrorImpl>,
19}
20
21/// Alias for a `Result` with the error type `serde_json::Error`.
22pub type Result<T> = result::Result<T, Error>;
23
24impl Error {
25    /// One-based line number at which the error was detected.
26    ///
27    /// Characters in the first line of the input (before the first newline
28    /// character) are in line 1.
29    pub fn line(&self) -> usize {
30        self.err.line
31    }
32
33    /// One-based column number at which the error was detected.
34    ///
35    /// The first character in the input and any characters immediately
36    /// following a newline character are in column 1.
37    ///
38    /// Note that errors may occur in column 0, for example if a read from an IO
39    /// stream fails immediately following a previously read newline character.
40    pub fn column(&self) -> usize {
41        self.err.column
42    }
43
44    /// Categorizes the cause of this error.
45    ///
46    /// - `Category::Io` - failure to read or write bytes on an IO stream
47    /// - `Category::Syntax` - input that is not syntactically valid JSON
48    /// - `Category::Data` - input data that is semantically incorrect
49    /// - `Category::Eof` - unexpected end of the input data
50    pub fn classify(&self) -> Category {
51        match self.err.code {
52            ErrorCode::Message(_) => Category::Data,
53            ErrorCode::Io(_) => Category::Io,
54            ErrorCode::EofWhileParsingList
55            | ErrorCode::EofWhileParsingObject
56            | ErrorCode::EofWhileParsingString
57            | ErrorCode::EofWhileParsingValue => Category::Eof,
58            ErrorCode::ExpectedColon
59            | ErrorCode::ExpectedListCommaOrEnd
60            | ErrorCode::ExpectedObjectCommaOrEnd
61            | ErrorCode::ExpectedObjectOrArray
62            | ErrorCode::ExpectedSomeIdent
63            | ErrorCode::ExpectedSomeValue
64            | ErrorCode::ExpectedSomeString
65            | ErrorCode::InvalidEscape
66            | ErrorCode::InvalidNumber
67            | ErrorCode::NumberOutOfRange
68            | ErrorCode::InvalidUnicodeCodePoint
69            | ErrorCode::ControlCharacterWhileParsingString
70            | ErrorCode::KeyMustBeAString
71            | ErrorCode::LoneLeadingSurrogateInHexEscape
72            | ErrorCode::TrailingComma
73            | ErrorCode::TrailingCharacters
74            | ErrorCode::UnexpectedEndOfHexEscape
75            | ErrorCode::RecursionLimitExceeded => Category::Syntax,
76        }
77    }
78
79    /// Returns true if this error was caused by a failure to read or write
80    /// bytes on an IO stream.
81    pub fn is_io(&self) -> bool {
82        self.classify() == Category::Io
83    }
84
85    /// Returns true if this error was caused by input that was not
86    /// syntactically valid JSON.
87    pub fn is_syntax(&self) -> bool {
88        self.classify() == Category::Syntax
89    }
90
91    /// Returns true if this error was caused by input data that was
92    /// semantically incorrect.
93    ///
94    /// For example, JSON containing a number is semantically incorrect when the
95    /// type being deserialized into holds a String.
96    pub fn is_data(&self) -> bool {
97        self.classify() == Category::Data
98    }
99
100    /// Returns true if this error was caused by prematurely reaching the end of
101    /// the input data.
102    ///
103    /// Callers that process streaming input may be interested in retrying the
104    /// deserialization once more data is available.
105    pub fn is_eof(&self) -> bool {
106        self.classify() == Category::Eof
107    }
108}
109
110/// Categorizes the cause of a `serde_json::Error`.
111#[derive(Copy, Clone, PartialEq, Eq, Debug)]
112pub enum Category {
113    /// The error was caused by a failure to read or write bytes on an IO
114    /// stream.
115    Io,
116
117    /// The error was caused by input that was not syntactically valid JSON.
118    Syntax,
119
120    /// The error was caused by input data that was semantically incorrect.
121    ///
122    /// For example, JSON containing a number is semantically incorrect when the
123    /// type being deserialized into holds a String.
124    Data,
125
126    /// The error was caused by prematurely reaching the end of the input data.
127    ///
128    /// Callers that process streaming input may be interested in retrying the
129    /// deserialization once more data is available.
130    Eof,
131}
132
133#[cfg_attr(feature = "cargo-clippy", allow(fallible_impl_from))]
134impl From<Error> for io::Error {
135    /// Convert a `serde_json::Error` into an `io::Error`.
136    ///
137    /// JSON syntax and data errors are turned into `InvalidData` IO errors.
138    /// EOF errors are turned into `UnexpectedEof` IO errors.
139    ///
140    /// ```edition2018
141    /// use std::io;
142    ///
143    /// enum MyError {
144    ///     Io(io::Error),
145    ///     Json(serde_json::Error),
146    /// }
147    ///
148    /// impl From<serde_json::Error> for MyError {
149    ///     fn from(err: serde_json::Error) -> MyError {
150    ///         use serde_json::error::Category;
151    ///         match err.classify() {
152    ///             Category::Io => {
153    ///                 MyError::Io(err.into())
154    ///             }
155    ///             Category::Syntax | Category::Data | Category::Eof => {
156    ///                 MyError::Json(err)
157    ///             }
158    ///         }
159    ///     }
160    /// }
161    /// ```
162    fn from(j: Error) -> Self {
163        if let ErrorCode::Io(err) = j.err.code {
164            err
165        } else {
166            match j.classify() {
167                Category::Io => unreachable!(),
168                Category::Syntax | Category::Data => io::Error::new(io::ErrorKind::InvalidData, j),
169                Category::Eof => io::Error::new(io::ErrorKind::UnexpectedEof, j),
170            }
171        }
172    }
173}
174
175struct ErrorImpl {
176    code: ErrorCode,
177    line: usize,
178    column: usize,
179}
180
181// Not public API. Should be pub(crate).
182#[doc(hidden)]
183pub enum ErrorCode {
184    /// Catchall for syntax error messages
185    Message(Box<str>),
186
187    /// Some IO error occurred while serializing or deserializing.
188    Io(io::Error),
189
190    /// EOF while parsing a list.
191    EofWhileParsingList,
192
193    /// EOF while parsing an object.
194    EofWhileParsingObject,
195
196    /// EOF while parsing a string.
197    EofWhileParsingString,
198
199    /// EOF while parsing a JSON value.
200    EofWhileParsingValue,
201
202    /// Expected this character to be a `':'`.
203    ExpectedColon,
204
205    /// Expected this character to be either a `','` or a `']'`.
206    ExpectedListCommaOrEnd,
207
208    /// Expected this character to be either a `','` or a `'}'`.
209    ExpectedObjectCommaOrEnd,
210
211    /// Expected this character to be either a `'{'` or a `'['`.
212    ExpectedObjectOrArray,
213
214    /// Expected to parse either a `true`, `false`, or a `null`.
215    ExpectedSomeIdent,
216
217    /// Expected this character to start a JSON value.
218    ExpectedSomeValue,
219
220    /// Expected this character to start a JSON string.
221    ExpectedSomeString,
222
223    /// Invalid hex escape code.
224    InvalidEscape,
225
226    /// Invalid number.
227    InvalidNumber,
228
229    /// Number is bigger than the maximum value of its type.
230    NumberOutOfRange,
231
232    /// Invalid unicode code point.
233    InvalidUnicodeCodePoint,
234
235    /// Control character found while parsing a string.
236    ControlCharacterWhileParsingString,
237
238    /// Object key is not a string.
239    KeyMustBeAString,
240
241    /// Lone leading surrogate in hex escape.
242    LoneLeadingSurrogateInHexEscape,
243
244    /// JSON has a comma after the last value in an array or map.
245    TrailingComma,
246
247    /// JSON has non-whitespace trailing characters after the value.
248    TrailingCharacters,
249
250    /// Unexpected end of hex excape.
251    UnexpectedEndOfHexEscape,
252
253    /// Encountered nesting of JSON maps and arrays more than 128 layers deep.
254    RecursionLimitExceeded,
255}
256
257impl Error {
258    // Not public API. Should be pub(crate).
259    #[doc(hidden)]
260    #[cold]
261    pub fn syntax(code: ErrorCode, line: usize, column: usize) -> Self {
262        Error {
263            err: Box::new(ErrorImpl {
264                code: code,
265                line: line,
266                column: column,
267            }),
268        }
269    }
270
271    // Not public API. Should be pub(crate).
272    //
273    // Update `eager_json` crate when this function changes.
274    #[doc(hidden)]
275    #[cold]
276    pub fn io(error: io::Error) -> Self {
277        Error {
278            err: Box::new(ErrorImpl {
279                code: ErrorCode::Io(error),
280                line: 0,
281                column: 0,
282            }),
283        }
284    }
285
286    // Not public API. Should be pub(crate).
287    #[doc(hidden)]
288    #[cold]
289    pub fn fix_position<F>(self, f: F) -> Self
290    where
291        F: FnOnce(ErrorCode) -> Error,
292    {
293        if self.err.line == 0 {
294            f(self.err.code)
295        } else {
296            self
297        }
298    }
299}
300
301impl Display for ErrorCode {
302    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
303        match *self {
304            ErrorCode::Message(ref msg) => f.write_str(msg),
305            ErrorCode::Io(ref err) => Display::fmt(err, f),
306            ErrorCode::EofWhileParsingList => f.write_str("EOF while parsing a list"),
307            ErrorCode::EofWhileParsingObject => f.write_str("EOF while parsing an object"),
308            ErrorCode::EofWhileParsingString => f.write_str("EOF while parsing a string"),
309            ErrorCode::EofWhileParsingValue => f.write_str("EOF while parsing a value"),
310            ErrorCode::ExpectedColon => f.write_str("expected `:`"),
311            ErrorCode::ExpectedListCommaOrEnd => f.write_str("expected `,` or `]`"),
312            ErrorCode::ExpectedObjectCommaOrEnd => f.write_str("expected `,` or `}`"),
313            ErrorCode::ExpectedObjectOrArray => f.write_str("expected `{` or `[`"),
314            ErrorCode::ExpectedSomeIdent => f.write_str("expected ident"),
315            ErrorCode::ExpectedSomeValue => f.write_str("expected value"),
316            ErrorCode::ExpectedSomeString => f.write_str("expected string"),
317            ErrorCode::InvalidEscape => f.write_str("invalid escape"),
318            ErrorCode::InvalidNumber => f.write_str("invalid number"),
319            ErrorCode::NumberOutOfRange => f.write_str("number out of range"),
320            ErrorCode::InvalidUnicodeCodePoint => f.write_str("invalid unicode code point"),
321            ErrorCode::ControlCharacterWhileParsingString => {
322                f.write_str("control character (\\u0000-\\u001F) found while parsing a string")
323            }
324            ErrorCode::KeyMustBeAString => f.write_str("key must be a string"),
325            ErrorCode::LoneLeadingSurrogateInHexEscape => {
326                f.write_str("lone leading surrogate in hex escape")
327            }
328            ErrorCode::TrailingComma => f.write_str("trailing comma"),
329            ErrorCode::TrailingCharacters => f.write_str("trailing characters"),
330            ErrorCode::UnexpectedEndOfHexEscape => f.write_str("unexpected end of hex escape"),
331            ErrorCode::RecursionLimitExceeded => f.write_str("recursion limit exceeded"),
332        }
333    }
334}
335
336impl error::Error for Error {
337    fn description(&self) -> &str {
338        match self.err.code {
339            ErrorCode::Io(ref err) => error::Error::description(err),
340            _ => {
341                // If you want a better message, use Display::fmt or to_string().
342                "JSON error"
343            }
344        }
345    }
346
347    fn cause(&self) -> Option<&error::Error> {
348        match self.err.code {
349            ErrorCode::Io(ref err) => Some(err),
350            _ => None,
351        }
352    }
353}
354
355impl Display for Error {
356    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
357        Display::fmt(&*self.err, f)
358    }
359}
360
361impl Display for ErrorImpl {
362    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
363        if self.line == 0 {
364            Display::fmt(&self.code, f)
365        } else {
366            write!(
367                f,
368                "{} at line {} column {}",
369                self.code, self.line, self.column
370            )
371        }
372    }
373}
374
375// Remove two layers of verbosity from the debug representation. Humans often
376// end up seeing this representation because it is what unwrap() shows.
377impl Debug for Error {
378    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
379        write!(
380            f,
381            "Error({:?}, line: {}, column: {})",
382            self.err.code.to_string(),
383            self.err.line,
384            self.err.column
385        )
386    }
387}
388
389impl de::Error for Error {
390    #[cold]
391    fn custom<T: Display>(msg: T) -> Error {
392        make_error(msg.to_string())
393    }
394
395    #[cold]
396    fn invalid_type(unexp: de::Unexpected, exp: &de::Expected) -> Self {
397        if let de::Unexpected::Unit = unexp {
398            Error::custom(format_args!("invalid type: null, expected {}", exp))
399        } else {
400            Error::custom(format_args!("invalid type: {}, expected {}", unexp, exp))
401        }
402    }
403}
404
405impl ser::Error for Error {
406    #[cold]
407    fn custom<T: Display>(msg: T) -> Error {
408        make_error(msg.to_string())
409    }
410}
411
412// Parse our own error message that looks like "{} at line {} column {}" to work
413// around erased-serde round-tripping the error through de::Error::custom.
414fn make_error(mut msg: String) -> Error {
415    let (line, column) = parse_line_col(&mut msg).unwrap_or((0, 0));
416    Error {
417        err: Box::new(ErrorImpl {
418            code: ErrorCode::Message(msg.into_boxed_str()),
419            line: line,
420            column: column,
421        }),
422    }
423}
424
425fn parse_line_col(msg: &mut String) -> Option<(usize, usize)> {
426    let start_of_suffix = match msg.rfind(" at line ") {
427        Some(index) => index,
428        None => return None,
429    };
430
431    // Find start and end of line number.
432    let start_of_line = start_of_suffix + " at line ".len();
433    let mut end_of_line = start_of_line;
434    while starts_with_digit(&msg[end_of_line..]) {
435        end_of_line += 1;
436    }
437
438    if !msg[end_of_line..].starts_with(" column ") {
439        return None;
440    }
441
442    // Find start and end of column number.
443    let start_of_column = end_of_line + " column ".len();
444    let mut end_of_column = start_of_column;
445    while starts_with_digit(&msg[end_of_column..]) {
446        end_of_column += 1;
447    }
448
449    if end_of_column < msg.len() {
450        return None;
451    }
452
453    // Parse numbers.
454    let line = match usize::from_str(&msg[start_of_line..end_of_line]) {
455        Ok(line) => line,
456        Err(_) => return None,
457    };
458    let column = match usize::from_str(&msg[start_of_column..end_of_column]) {
459        Ok(column) => column,
460        Err(_) => return None,
461    };
462
463    msg.truncate(start_of_suffix);
464    Some((line, column))
465}
466
467fn starts_with_digit(slice: &str) -> bool {
468    match slice.as_bytes().get(0) {
469        None => false,
470        Some(&byte) => byte >= b'0' && byte <= b'9',
471    }
472}