/// <summary> /// Internal init constuctor. /// </summary> internal JSonReaderException(string message, JSonReaderTokenInfo token) : base(PrepareMessage(message, token)) { InvalidToken = token != null ? token.Text : string.Empty; Line = token != null ? token.Line : 0; Offset = token != null ? token.Offset : 0; }
/// <summary> /// Reads a keyword from the input stream. /// </summary> private object ReadKeyword() { JSonReaderTokenInfo topToken = PopTopToken(); // top token contains the first letter of current keyword: StringBuilder buffer = new StringBuilder(topToken.Text); int lastLine = _input.Line; int lastOffset = _input.LineOffset; StringHelper.ReadKeywordChars(_input, buffer); // since keyword has no closing token (as arrays or strings), it might // happen that we read too many chars... so put that new char as a token on the // stack and instruct reader that token is already there... ReadNextToken(char.IsWhiteSpace(_input.CurrentChar)); _getTokenFromStack = true; string keyword = buffer.ToString().ToLower(); foreach (var k in AvailableKeywords) { if (k.Token == keyword) { return(_factory.CreateKeyword(k)); } } // token has not been found: throw new JSonReaderException("Unknown keyword", keyword, lastLine, lastOffset); }
private static string PrepareMessage(string message, JSonReaderTokenInfo token) { if (token != null) { return(PrepareMessage(message, token.Text, token.Line, token.Offset)); } return(PrepareMessage(message, string.Empty, 0, 0)); }
/// <summary> /// Read number from an input stream. /// </summary> private object ReadNumber() { JSonReaderTokenInfo topToken = PopTopToken(); // top token contains the first letter of current number: StringBuilder buffer = new StringBuilder(topToken.Text); int numberOffset = _input.LineOffset; StringHelper.ReadDecimalNumberChars(_input, buffer); // verify what kind of character is just after the number, if it's a letter then it is an error, // the only allowed are white-chars and JSON object separators (comma, ']', '}')! if (!_input.IsEof) { if (_input.CurrentChar != ',' && !char.IsWhiteSpace(_input.CurrentChar) && _input.CurrentChar != ']' && _input.CurrentChar != '}') { buffer.Append(_input.CurrentChar); throw new JSonReaderException("Invalid number", buffer.ToString(), _input.Line, numberOffset); } } // since number has no closing token (as arrays or strings), it might // happen that we read too many chars... so put that new char as a token on the // stack and instruct reader that token is already there... ReadNextToken(char.IsWhiteSpace(_input.CurrentChar)); _getTokenFromStack = true; string number = buffer.ToString(); object result = _factory.CreateNumber(number); if (result != null) { return(result); } // number had some invalid format: throw new JSonReaderException(string.Concat("Invalid number format, value: \"", number, "\", expected format: ", _factory.Format), number, _input.Line, numberOffset); }
private static string PrepareMessage(string message, JSonReaderTokenInfo token) { if (token != null) return PrepareMessage(message, token.Text, token.Line, token.Offset); return PrepareMessage(message, string.Empty, 0, 0); }
/// <summary> /// Adds value to the dictionary. /// </summary> private static void AddValue(IDictionary<string, object> result, ref string name, object value, ref bool colonSpot, ref bool commaSpot, JSonReaderTokenInfo currentToken) { if (!colonSpot) throw new JSonReaderException("Missing colon between name and value definition", currentToken); if (!commaSpot && result.Count > 0) throw new JSonReaderException("Missing comma before name and value definition", currentToken); if (name == null) throw new JSonReaderException("Missing value for object element", currentToken); if (result.ContainsKey(name)) throw new JSonReaderException("Duplicated name in object", currentToken); result.Add(name, value); name = null; colonSpot = false; commaSpot = false; }
/// <summary> /// Adds new element to an array. /// </summary> private static void AddValue(ICollection<object> result, object value, int commas, JSonReaderTokenInfo currentToken) { if (result.Count != commas) throw new JSonReaderException("Missing commas between array objects", currentToken); result.Add(value); }
/// <summary> /// Reads next token from the input source with a possibility to treat already known character as the one read. /// Option required mostly for JSON elements that don't have a closing tokens (i.e.: ']' for arrays) and used for number and keywords. /// </summary> private JSonReaderTokenInfo ReadNextToken(bool fromCurrentChar) { // token was already read in advanced and put on the stack: if (_getTokenFromStack) { _getTokenFromStack = false; // reached end of input stream: if (_input.IsEof) return new JSonReaderTokenInfo(string.Empty, JSonReaderTokenType.EndOfText, _input.Line, _input.LineOffset); if (_tokens.Count == 0) throw new JSonReaderException("Lack of tokens", (JSonReaderTokenInfo)null); return _tokens.Peek(); } if (fromCurrentChar) { // clear the whitespace characters: StringHelper.ReadWhiteChars(_input); } // reached end of input stream: if (_input.IsEof) return new JSonReaderTokenInfo(string.Empty, JSonReaderTokenType.EndOfText, _input.Line, _input.LineOffset); char tokenChar = _input.CurrentChar; string tokenString = tokenChar.ToString(); JSonReaderTokenType tokenType = JSonReaderTokenType.Unknown; // check if this is one of the already known tokens... foreach (var tokenDef in AvailableTokens) if (tokenDef.Token == tokenChar) { tokenType = tokenDef.Type; break; } // is this the beginning of a keyword? if (tokenType == JSonReaderTokenType.Unknown && char.IsLetter(tokenChar)) tokenType = JSonReaderTokenType.Keyword; // is this the beginning of the number? if (tokenType == JSonReaderTokenType.Unknown && (char.IsDigit(tokenChar) || tokenChar == '-')) tokenType = JSonReaderTokenType.Number; // if this is still unknown element... if (tokenType == JSonReaderTokenType.Unknown) throw new JSonReaderException("Invalid token found", tokenString, _input.Line, _input.LineOffset); JSonReaderTokenInfo nextToken = new JSonReaderTokenInfo(tokenString, tokenType, _input.Line, _input.LineOffset); _tokens.Push(nextToken); // return it: return nextToken; }
/// <summary> /// Reads a dictonary from an input stream. /// </summary> private object ReadObject() { Dictionary <string, object> result = new Dictionary <string, object>(); JSonReaderTokenInfo currentToken; string name = null; bool colonSpot = false; bool commaSpot = false; int commas = 0; while ((currentToken = ReadNextToken(true)).Type != JSonReaderTokenType.EndOfText) { if (currentToken.Type == JSonReaderTokenType.ObjectEnd) { PopTopToken(); // if number of commas is greater than number of added elements, // then value was not passed between: if (commas > 0 && commas >= result.Count) { throw new JSonReaderException("Too many commas at closing object token, expected none", currentToken); } break; } switch (currentToken.Type) { case JSonReaderTokenType.ArrayStart: // read embedded array: AddValue(result, ref name, ReadArray(), ref colonSpot, ref commaSpot, currentToken); break; case JSonReaderTokenType.ObjectStart: // read embedded object: AddValue(result, ref name, ReadObject(), ref colonSpot, ref commaSpot, currentToken); break; case JSonReaderTokenType.Keyword: if (name == null) { throw new JSonReaderException("Keyword can not be an object element's name", currentToken); } // add embedded value of reserved keyword: AddValue(result, ref name, ReadKeyword(), ref colonSpot, ref commaSpot, currentToken); break; case JSonReaderTokenType.Number: // add embedded numeric value: AddValue(result, ref name, ReadNumber(), ref colonSpot, ref commaSpot, currentToken); break; case JSonReaderTokenType.String: if (!commaSpot && result.Count > 0) { throw new JSonReaderException("Missing comma before name and value definition", currentToken); } if (name == null) { name = ReadString().ToString(); if (result.ContainsKey(name)) { throw new JSonReaderException("Duplicated name in object", currentToken); } break; } // add embedded string: AddValue(result, ref name, ReadString(), ref colonSpot, ref commaSpot, currentToken); break; case JSonReaderTokenType.Colon: PopTopToken(); if (colonSpot) { throw new JSonReaderException("Duplicated colon found in object", currentToken); } if (name == null) { throw new JSonReaderException("Unexpected colon, when name not given", currentToken); } colonSpot = true; break; case JSonReaderTokenType.Comma: // go to next array element: currentToken = PopTopToken(); if (commaSpot) { throw new JSonReaderException("Two commans in a row", currentToken); } commas++; commaSpot = true; // if number of commas is greater than number of added elements, // then value was not passed between: if (commas > result.Count) { throw new JSonReaderException("Missing value for object element", currentToken); } break; default: throw new JSonReaderException("Invalid token", currentToken); } } JSonReaderTokenInfo topToken = PopTopToken(); if (topToken.Type != JSonReaderTokenType.ObjectStart || currentToken.Type == JSonReaderTokenType.EndOfText) { throw new JSonReaderException("Missing close object token", topToken); } return(_factory.CreateObject(result)); }
/// <summary> /// Adds value to the dictionary. /// </summary> private static void AddValue(IDictionary <string, object> result, ref string name, object value, ref bool colonSpot, ref bool commaSpot, JSonReaderTokenInfo currentToken) { if (!colonSpot) { throw new JSonReaderException("Missing colon between name and value definition", currentToken); } if (!commaSpot && result.Count > 0) { throw new JSonReaderException("Missing comma before name and value definition", currentToken); } if (name == null) { throw new JSonReaderException("Missing value for object element", currentToken); } if (result.ContainsKey(name)) { throw new JSonReaderException("Duplicated name in object", currentToken); } result.Add(name, value); name = null; colonSpot = false; commaSpot = false; }
/// <summary> /// Read an array from input stream. /// </summary> private object ReadArray() { List <object> result = new List <object>(); JSonReaderTokenInfo currentToken; int commas = 0; while ((currentToken = ReadNextToken(true)).Type != JSonReaderTokenType.EndOfText) { if (currentToken.Type == JSonReaderTokenType.ArrayEnd) { PopTopToken(); // if number of commas is greater than number of added elements, // then value was not passed between: if (result.Count > 0 && commas != result.Count - 1) { throw new JSonReaderException("Too many commas at closing array token, expected none", currentToken); } break; } switch (currentToken.Type) { case JSonReaderTokenType.ArrayStart: // read embedded array: AddValue(result, ReadArray(), commas, currentToken); break; case JSonReaderTokenType.ObjectStart: // read embedded object: AddValue(result, ReadObject(), commas, currentToken); break; case JSonReaderTokenType.Keyword: // add embedded value of reserved keyword: AddValue(result, ReadKeyword(), commas, currentToken); break; case JSonReaderTokenType.Number: // add embedded numeric value: AddValue(result, ReadNumber(), commas, currentToken); break; case JSonReaderTokenType.String: // add embedded string value: AddValue(result, ReadString(), commas, currentToken); break; case JSonReaderTokenType.Comma: // go to next array element: currentToken = PopTopToken(); commas++; // if number of commas is greater than number of added elements, // then value was not passed between: if (commas > result.Count) { throw new JSonReaderException("Missing value for array element", currentToken); } break; default: throw new JSonReaderException("Invalid token", currentToken); } } JSonReaderTokenInfo topToken = PopTopToken(); if (topToken.Type != JSonReaderTokenType.ArrayStart || currentToken.Type == JSonReaderTokenType.EndOfText) { throw new JSonReaderException("Missing close array token", topToken); } return(_factory.CreateArray(result)); }
/// <summary> /// Adds new element to an array. /// </summary> private static void AddValue(ICollection <object> result, object value, int commas, JSonReaderTokenInfo currentToken) { if (result.Count != commas) { throw new JSonReaderException("Missing commas between array objects", currentToken); } result.Add(value); }
/// <summary> /// Reads next token from the input source with a possibility to treat already known character as the one read. /// Option required mostly for JSON elements that don't have a closing tokens (i.e.: ']' for arrays) and used for number and keywords. /// </summary> private JSonReaderTokenInfo ReadNextToken(bool fromCurrentChar) { // token was already read in advanced and put on the stack: if (_getTokenFromStack) { _getTokenFromStack = false; // reached end of input stream: if (_input.IsEof) { return(new JSonReaderTokenInfo(string.Empty, JSonReaderTokenType.EndOfText, _input.Line, _input.LineOffset)); } if (_tokens.Count == 0) { throw new JSonReaderException("Lack of tokens", (JSonReaderTokenInfo)null); } return(_tokens.Peek()); } if (fromCurrentChar) { // clear the whitespace characters: StringHelper.ReadWhiteChars(_input); } // reached end of input stream: if (_input.IsEof) { return(new JSonReaderTokenInfo(string.Empty, JSonReaderTokenType.EndOfText, _input.Line, _input.LineOffset)); } char tokenChar = _input.CurrentChar; string tokenString = tokenChar.ToString(); JSonReaderTokenType tokenType = JSonReaderTokenType.Unknown; // check if this is one of the already known tokens... foreach (var tokenDef in AvailableTokens) { if (tokenDef.Token == tokenChar) { tokenType = tokenDef.Type; break; } } // is this the beginning of a keyword? if (tokenType == JSonReaderTokenType.Unknown && char.IsLetter(tokenChar)) { tokenType = JSonReaderTokenType.Keyword; } // is this the beginning of the number? if (tokenType == JSonReaderTokenType.Unknown && (char.IsDigit(tokenChar) || tokenChar == '-')) { tokenType = JSonReaderTokenType.Number; } // if this is still unknown element... if (tokenType == JSonReaderTokenType.Unknown) { throw new JSonReaderException("Invalid token found", tokenString, _input.Line, _input.LineOffset); } JSonReaderTokenInfo nextToken = new JSonReaderTokenInfo(tokenString, tokenType, _input.Line, _input.LineOffset); _tokens.Push(nextToken); // return it: return(nextToken); }