/** * Attempts to read a number from the input string. Places * the character location at the first character after the * number. * * @return The JSONToken with the number value if a number could * be read. Throws an error otherwise. */ private JSONToken readNumber() { // the string to accumulate the number characters // into that we'll convert to a number at the end string input = ""; // check for a negative number if (ch == '-') { input += '-'; nextChar(); } // the number must start with a digit if (!isDigit(ch)) { parseError("Expecting a digit"); } // 0 can only be the first digit if it // is followed by a decimal point if (ch == '0') { input += ch; nextChar(); // make sure no other digits come after 0 if (isDigit(ch)) { parseError("A digit cannot immediately follow 0"); } // unless we have 0x which starts a hex number, but this // doesn't match JSON spec so check for not strict mode. else { if (!strict && ch == 'x') { // include the x in the input input += ch; nextChar(); // need at least one hex digit after 0x to // be valid if (isHexDigit(ch)) { input += ch; nextChar(); } else { parseError("Number in hex format require at least one hex digit after \"0x\""); } // consume all of the hex values while (isHexDigit(ch)) { input += ch; nextChar(); } input = Convert.ToInt32(input, 16).ToString(); } } } else { // read numbers while we can while (isDigit(ch)) { input += ch; nextChar(); } } // check for a decimal value if (ch == '.') { input += '.'; nextChar(); // after the decimal there has to be a digit if (!isDigit(ch)) { parseError("Expecting a digit"); } // read more numbers to get the decimal value while (isDigit(ch)) { input += ch; nextChar(); } } // check for scientific notation if (ch == 'e' || ch == 'E') { input += "e"; nextChar(); // check for sign if (ch == '+' || ch == '-') { input += ch; nextChar(); } // require at least one number for the exponent // in this case if (!isDigit(ch)) { parseError("Scientific notation number needs exponent value"); } // read in the exponent while (isDigit(ch)) { input += ch; nextChar(); } } // convert the string to a number value int int_num; bool correct_int = false; try { int_num = Convert.ToInt32(input); correct_int = true; } catch (FormatException) { int_num = 0; correct_int = false; } long long_num; bool correct_long = false; try { long_num = Convert.ToInt64(input); correct_long = true; } catch (FormatException) { long_num = 0; correct_long = false; } float float_num = Convert.ToSingle(input, CultureInfo.InvariantCulture); if (!float.IsInfinity(float_num) && !float.IsNaN(float_num)) { // the token for the number we'll try to read JSONToken token = new JSONToken(); token.type = JSONTokenType.NUMBER; if (correct_long && float_num == Convert.ToSingle(long_num)) // the number is integer { if (correct_int && long_num == Convert.ToInt64(int_num)) // the number is int32 { token.value = int_num; // boxing int to object } else // the number is int64 { token.value = long_num; // boxing long int to object } } else // the number is float { token.value = float_num; } return(token); } else { parseError("Number " + float_num + " is not valid!"); } return(null); }
/** * Gets the next token in the input sting and advances * the character to the next character after the token */ public JSONToken getNextToken() { JSONToken token = null; // skip any whitespace / comments since the last // token was read skipIgnored(); // examine the new character and see what we have... switch (ch) { case '{': token = new JSONToken(JSONTokenType.LEFT_BRACE, '{'); nextChar(); break; case '}': token = new JSONToken(JSONTokenType.RIGHT_BRACE, '}'); nextChar(); break; case '[': token = new JSONToken(JSONTokenType.LEFT_BRACKET, '['); nextChar(); break; case ']': token = new JSONToken(JSONTokenType.RIGHT_BRACKET, ']'); nextChar(); break; case ',': token = new JSONToken(JSONTokenType.COMMA, ','); nextChar(); break; case ':': token = new JSONToken(JSONTokenType.COLON, ':'); nextChar(); break; case 't': // attempt to read true string possibleTrue = "t" + nextChar() + nextChar() + nextChar(); if (possibleTrue == "true") { token = new JSONToken(JSONTokenType.TRUE, true); nextChar(); } else { parseError("Expecting 'true' but found " + possibleTrue); } break; case 'f': // attempt to read false string possibleFalse = "f" + nextChar() + nextChar() + nextChar() + nextChar(); if (possibleFalse == "false") { token = new JSONToken(JSONTokenType.FALSE, false); nextChar(); } else { parseError("Expecting 'false' but found " + possibleFalse); } break; case 'n': // attempt to read null string possibleNull = "n" + nextChar() + nextChar() + nextChar(); if (possibleNull == "null") { token = new JSONToken(JSONTokenType.NULL, null); nextChar(); } else { parseError("Expecting 'null' but found " + possibleNull); } break; case 'N': //attempt to read NAN string possibleNAN = "N" + nextChar() + nextChar(); if (possibleNAN == "NAN" || possibleNAN == "NaN") { token = new JSONToken(JSONTokenType.NAN, float.NaN); nextChar(); } else { parseError("Expecting 'nan' but found " + possibleNAN); } break; case '"': // the start of a string token = readString(); break; default: // see if we can read a number if (isDigit(ch) || ch == '-') { token = readNumber(); } else if (ch == 0) { // check for reading past the end of the string return(null); } else { // not sure what was in the input string - it's not // anything we expected parseError("Unexpected " + ch + " encountered"); } break; } return(token); }
/** * Attempts to read a string from the input string. Places * the character location at the first character after the * string. It is assumed that ch is " before this method is called. * * @return the JSONToken with the string value if a string could * be read. Throws an error otherwise. */ private JSONToken readString() { // the string to store the string we'll try to read string str = ""; // advance past the first " nextChar(); while (ch != '"' && ch != 0) { //trace(ch); // unescape the escape sequences in the string if (ch == '\\') { // get the next character so we know what // to unescape nextChar(); switch (ch) { case '"': // quotation mark str += '"'; break; case '/': // solidus str += "/"; break; case '\\': // reverse solidus str += '\\'; break; case 'n': // newline str += '\n'; break; case 'r': // carriage return str += '\r'; break; case 't': // horizontal tab str += '\t'; break; case 'u': // convert a unicode escape sequence // to it's character value - expecting // 4 hex digits // save the characters as a string we'll convert to an int string hexValue = ""; // try to find 4 hex characters for (int i = 0; i < 4; i++) { // get the next character and determine // if it's a valid hex digit or not if (!isHexDigit(nextChar())) { parseError(" Excepted a hex digit, but found: " + ch); } // valid, add it to the value hexValue += ch; } // convert hexValue to an integer, and use that // integrer value to create a character to add // to our string. str += ((char)Convert.ToInt32(hexValue, 16)).ToString(); break; default: // couldn't unescape the sequence, so just // pass it through str += '\\' + ch; break; } } else { // didn't have to unescape, so add the character to the string str += ch; } // move to the next character nextChar(); } // we read past the end of the string without closing it, which // is a parse error if (ch == 0) { parseError("Unterminated string literal"); } // move past the closing " in the input string nextChar(); // the token for the string we'll try to read JSONToken token = new JSONToken(); token.type = JSONTokenType.STRING; // attach to the string to the token so we can return it token.value = str; return(token); }
private JSONToken nextToken() { return(token = tokenizer.getNextToken()); }