/// <summary> /// Constructs a new JsonParseException /// </summary> /// <param name="inner">The inner exception</param> /// <param name="context">A string describing the context of the serialization (parent key path)</param> /// <param name="position">The position in the JSON stream where the error occured</param> public JsonParseException(Exception inner, string context, LineOffset position) : base(string.Format("JSON parse error at {0}{1} - {2}", position, string.IsNullOrEmpty(context) ? "" : string.Format(", context {0}", context), inner.Message), inner) { Position = position; Context = context; }
// Read the next token from the input stream // (Mostly inline for performance) public void NextToken() { while (true) { // Skip whitespace and handle line numbers while (true) { if (_currentChar == '\r') { if (NextChar() == '\n') { NextChar(); } _currentCharPos.Line++; _currentCharPos.Offset = 0; } else if (_currentChar == '\n') { if (NextChar() == '\r') { NextChar(); } _currentCharPos.Line++; _currentCharPos.Offset = 0; } else if (_currentChar == ' ') { NextChar(); } else if (_currentChar == '\t') { NextChar(); } else { break; } } // Remember position of token CurrentTokenPosition = _currentCharPos; // Handle common characters first switch (_currentChar) { case '/': // Comments not support in strict mode if ((_options & JsonOptions.StrictParser) != 0) { throw new InvalidDataException(string.Format("syntax error, unexpected character '{0}'", _currentChar)); } // Process comment NextChar(); switch (_currentChar) { case '/': NextChar(); while (_currentChar != '\0' && _currentChar != '\r' && _currentChar != '\n') { NextChar(); } break; case '*': bool endFound = false; while (!endFound && _currentChar != '\0') { if (_currentChar == '*') { NextChar(); if (_currentChar == '/') { endFound = true; } } NextChar(); } break; default: throw new InvalidDataException("syntax error, unexpected character after slash"); } continue; case '\"': case '\'': { _sb.Length = 0; var quoteKind = _currentChar; NextChar(); while (_currentChar != '\0') { if (_currentChar == '\\') { NextChar(); var escape = _currentChar; switch (escape) { case '\"': _sb.Append('\"'); break; case '\\': _sb.Append('\\'); break; case '/': _sb.Append('/'); break; case 'b': _sb.Append('\b'); break; case 'f': _sb.Append('\f'); break; case 'n': _sb.Append('\n'); break; case 'r': _sb.Append('\r'); break; case 't': _sb.Append('\t'); break; case 'u': var sbHex = new StringBuilder(); for (int i = 0; i < 4; i++) { NextChar(); sbHex.Append(_currentChar); } _sb.Append((char)Convert.ToUInt16(sbHex.ToString(), 16)); break; default: throw new InvalidDataException(string.Format("Invalid escape sequence in string literal: '\\{0}'", _currentChar)); } } else if (_currentChar == quoteKind) { String = _sb.ToString(); CurrentToken = Token.Literal; LiteralKind = LiteralKind.String; NextChar(); return; } else { _sb.Append(_currentChar); } NextChar(); } throw new InvalidDataException("syntax error, unterminated string literal"); } case '{': CurrentToken = Token.OpenBrace; NextChar(); return; case '}': CurrentToken = Token.CloseBrace; NextChar(); return; case '[': CurrentToken = Token.OpenSquare; NextChar(); return; case ']': CurrentToken = Token.CloseSquare; NextChar(); return; case '=': CurrentToken = Token.Equal; NextChar(); return; case ':': CurrentToken = Token.Colon; NextChar(); return; case ';': CurrentToken = Token.SemiColon; NextChar(); return; case ',': CurrentToken = Token.Comma; NextChar(); return; case '\0': CurrentToken = Token.EOF; return; } // Number? if (char.IsDigit(_currentChar) || _currentChar == '-') { TokenizeNumber(); return; } // Identifier? (checked for after everything else as identifiers are actually quite rare in valid json) if (Char.IsLetter(_currentChar) || _currentChar == '_' || _currentChar == '$') { // Find end of identifier _sb.Length = 0; while (Char.IsLetterOrDigit(_currentChar) || _currentChar == '_' || _currentChar == '$') { _sb.Append(_currentChar); NextChar(); } String = _sb.ToString(); // Handle special identifiers switch (String) { case "true": LiteralKind = LiteralKind.True; CurrentToken = Token.Literal; return; case "false": LiteralKind = LiteralKind.False; CurrentToken = Token.Literal; return; case "null": LiteralKind = LiteralKind.Null; CurrentToken = Token.Literal; return; } CurrentToken = Token.Identifier; return; } // What the? throw new InvalidDataException(string.Format("syntax error, unexpected character '{0}'", _currentChar)); } }