/** * Extracts a JSON String as defined by the spec - "<some chars>" * Any escaped characters are swapped out for their unescaped values * * @access protected * * @param wchar_t** data Pointer to a wchar_t* that contains the JSON text * @param std::wstring& str Reference to a std::wstring to receive the extracted string * * @return bool Returns true on success, false on failure */ bool JSON::ExtractString(const wchar_t **data, std::wstring &str) { str = L""; while (**data != 0) { // Save the char so we can change it if need be wchar_t next_char = **data; // Escaping something? if (next_char == L'\\') { // Move over the escape char (*data)++; // Deal with the escaped char switch (**data) { case L'"': next_char = L'"'; break; case L'\\': next_char = L'\\'; break; case L'/': next_char = L'/'; break; case L'b': next_char = L'\b'; break; case L'f': next_char = L'\f'; break; case L'n': next_char = L'\n'; break; case L'r': next_char = L'\r'; break; case L't': next_char = L'\t'; break; case L'u': { // We need 5 chars (4 hex + the 'u') or its not valid if (!simplejson_wcsnlen(*data, 5)) return false; // Deal with the chars next_char = 0; for (int i = 0; i < 4; i++) { // Do it first to move off the 'u' and leave us on the // final hex digit as we move on by one later on (*data)++; next_char <<= 4; // Parse the hex digit if (**data >= '0' && **data <= '9') next_char |= (**data - '0'); else if (**data >= 'A' && **data <= 'F') next_char |= (10 + (**data - 'A')); else if (**data >= 'a' && **data <= 'f') next_char |= (10 + (**data - 'a')); else { // Invalid hex digit = invalid JSON return false; } } break; } // By the spec, only the above cases are allowed default: return false; } } // End of the string? else if (next_char == L'"') { (*data)++; str.reserve(); // Remove unused capacity return true; } // Disallowed char? else if (next_char < L' ' && next_char != L'\t') { // SPEC Violation: Allow tabs due to real world cases return false; } // Add the next char str += next_char; // Move on (*data)++; } // If we're here, the string ended incorrectly return false; }
/** * Parses a JSON encoded value to a JSONValue object * * @access protected * * @param wchar_t** data Pointer to a wchar_t* that contains the data * * @return JSONValue* Returns a pointer to a JSONValue object on success, NULL on error */ JSONValue *JSONValue::Parse(const wchar_t **data) { // Is it a string? if (**data == '"') { std::wstring str; if (!JSON::ExtractString(&(++(*data)), str)) return NULL; else return new JSONValue(str); } // Is it a boolean? else if ((simplejson_wcsnlen(*data, 4) && wcsncasecmp(*data, L"true", 4) == 0) || (simplejson_wcsnlen(*data, 5) && wcsncasecmp(*data, L"false", 5) == 0)) { bool value = wcsncasecmp(*data, L"true", 4) == 0; (*data) += value ? 4 : 5; return new JSONValue(value); } // Is it a null? else if (simplejson_wcsnlen(*data, 4) && wcsncasecmp(*data, L"null", 4) == 0) { (*data) += 4; return new JSONValue(); } // Is it a number? else if (**data == L'-' || (**data >= L'0' && **data <= L'9')) { // Negative? bool neg = **data == L'-'; if (neg) (*data)++; double number = 0.0; // Parse the whole part of the number - only if it wasn't 0 if (**data == L'0') (*data)++; else if (**data >= L'1' && **data <= L'9') number = JSON::ParseInt(data); else return NULL; // Could be a decimal now... if (**data == '.') { (*data)++; // Not get any digits? if (!(**data >= L'0' && **data <= L'9')) return NULL; // Find the decimal and sort the decimal place out // Use ParseDecimal as ParseInt won't work with decimals less than 0.1 // thanks to Javier Abadia for the report & fix double decimal = JSON::ParseDecimal(data); // Save the number number += decimal; } // Could be an exponent now... if (**data == L'E' || **data == L'e') { (*data)++; // Check signage of expo bool neg_expo = false; if (**data == L'-' || **data == L'+') { neg_expo = **data == L'-'; (*data)++; } // Not get any digits? if (!(**data >= L'0' && **data <= L'9')) return NULL; // Sort the expo out double expo = JSON::ParseInt(data); for (double i = 0.0; i < expo; i++) number = neg_expo ? (number / 10.0) : (number * 10.0); } // Was it neg? if (neg) number *= -1; return new JSONValue(number); } // An object? else if (**data == L'{') { JSONObject object; (*data)++; while (**data != 0) { // Whitespace at the start? if (!JSON::SkipWhitespace(data)) { FREE_OBJECT(object); return NULL; } // Special case - empty object if (object.size() == 0 && **data == L'}') { (*data)++; return new JSONValue(object); } // We want a string now... std::wstring name; if (!JSON::ExtractString(&(++(*data)), name)) { FREE_OBJECT(object); return NULL; } // More whitespace? if (!JSON::SkipWhitespace(data)) { FREE_OBJECT(object); return NULL; } // Need a : now if (*((*data)++) != L':') { FREE_OBJECT(object); return NULL; } // More whitespace? if (!JSON::SkipWhitespace(data)) { FREE_OBJECT(object); return NULL; } // The value is here JSONValue *value = Parse(data); if (value == NULL) { FREE_OBJECT(object); return NULL; } // Add the name:value if (object.find(name) != object.end()) delete object[name]; object[name] = value; // More whitespace? if (!JSON::SkipWhitespace(data)) { FREE_OBJECT(object); return NULL; } // End of object? if (**data == L'}') { (*data)++; return new JSONValue(object); } // Want a , now if (**data != L',') { FREE_OBJECT(object); return NULL; } (*data)++; } // Only here if we ran out of data FREE_OBJECT(object); return NULL; } // An array? else if (**data == L'[') { JSONArray array; (*data)++; while (**data != 0) { // Whitespace at the start? if (!JSON::SkipWhitespace(data)) { FREE_ARRAY(array); return NULL; } // Special case - empty array if (array.size() == 0 && **data == L']') { (*data)++; return new JSONValue(array); } // Get the value JSONValue *value = Parse(data); if (value == NULL) { FREE_ARRAY(array); return NULL; } // Add the value array.push_back(value); // More whitespace? if (!JSON::SkipWhitespace(data)) { FREE_ARRAY(array); return NULL; } // End of array? if (**data == L']') { (*data)++; return new JSONValue(array); } // Want a , now if (**data != L',') { FREE_ARRAY(array); return NULL; } (*data)++; } // Only here if we ran out of data FREE_ARRAY(array); return NULL; } // Ran out of possibilites, it's bad! else { return NULL; } }