| /******************************************************************************* |
| * Copyright (c) 2011 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| |
| |
| #include "StdAfx.h" |
| #include "JSONParser.h" |
| |
| /* initialize statics */ |
| const wchar_t* JSONParser::VALUE_NULL = L"null"; |
| const wchar_t* JSONParser::VALUE_TRUE = L"true"; |
| const wchar_t* JSONParser::VALUE_FALSE = L"false"; |
| |
| JSONParser::JSONParser(void) { |
| } |
| |
| JSONParser::~JSONParser(void) { |
| } |
| |
| void JSONParser::parse(std::wstring* jsonString, Value** _value) { |
| *_value = NULL; |
| |
| std::wstringstream jsonStream(*jsonString); |
| parse(&jsonStream, _value); |
| } |
| |
| void JSONParser::parse(std::wstringstream* jsonStream, Value** _value) { |
| *_value = NULL; |
| |
| skipWhitespace(jsonStream); |
| wchar_t firstChar = jsonStream->peek(); |
| if (firstChar == wchar_t('n')) { |
| size_t length = wcslen(VALUE_NULL); |
| wchar_t* streamChars = new wchar_t[length]; |
| jsonStream->read(streamChars, (std::streamsize)length); |
| if (wcsncmp(VALUE_NULL, streamChars, length) == 0) { |
| *_value = new Value(); |
| (*_value)->setType(TYPE_NULL); |
| } else { |
| Logger::error("JSON string has invalid value that starts with 'n' but is not \"null\""); |
| } |
| delete[] streamChars; |
| return; |
| } |
| if (firstChar == wchar_t('t')) { |
| size_t length = wcslen(VALUE_TRUE); |
| wchar_t* streamChars = new wchar_t[length]; |
| jsonStream->read(streamChars, (std::streamsize)length); |
| if (wcsncmp(VALUE_TRUE, streamChars, length) == 0) { |
| Value* result = new Value(true); |
| *_value = result; |
| } else { |
| Logger::error("JSON string has invalid value that starts with 't' but is not \"true\""); |
| } |
| delete[] streamChars; |
| return; |
| } |
| if (firstChar == wchar_t('f')) { |
| size_t length = wcslen(VALUE_FALSE); |
| wchar_t* streamChars = new wchar_t[length]; |
| jsonStream->read(streamChars, (std::streamsize)length); |
| if (wcsncmp(VALUE_FALSE, streamChars, length) == 0) { |
| Value* result = new Value(false); |
| *_value = result; |
| } else { |
| Logger::error("JSON string has invalid value that starts with 'f' but is not \"false\""); |
| } |
| delete[] streamChars; |
| return; |
| } |
| if (firstChar == wchar_t('\"')) { |
| std::wstring* stringValue = NULL; |
| parseString(jsonStream, &stringValue); |
| if (stringValue) { |
| Value* result = new Value(stringValue); |
| *_value = result; |
| delete stringValue; |
| } else { |
| /* parseString() already logs a detailed error message, so no error logged here */ |
| } |
| return; |
| } |
| |
| int index = 0; |
| switch (firstChar) { |
| case wchar_t('0'): |
| case wchar_t('1'): |
| case wchar_t('2'): |
| case wchar_t('3'): |
| case wchar_t('4'): |
| case wchar_t('5'): |
| case wchar_t('6'): |
| case wchar_t('7'): |
| case wchar_t('8'): |
| case wchar_t('9'): |
| case wchar_t('-'): { |
| double doubleValue = parseNumber(jsonStream); |
| if (doubleValue == -DBL_MAX) { |
| Logger::error("JSON string has a Number value with invalid character(s)"); |
| return; |
| } |
| Value* result = new Value(doubleValue); |
| *_value = result; |
| return; |
| } |
| } |
| |
| if (firstChar == wchar_t('[')) { |
| parseArray(jsonStream, _value); |
| return; |
| } |
| |
| if (firstChar == wchar_t('{')) { |
| parseObject(jsonStream, _value); |
| return; |
| } |
| } |
| |
| void JSONParser::parseArray(std::wstringstream* jsonStream, Value** _value) { |
| *_value = NULL; |
| |
| Value* result = new Value(); |
| result->setType(TYPE_ARRAY); |
| |
| jsonStream->ignore(1); |
| skipWhitespace(jsonStream); |
| if (jsonStream->peek() == wchar_t(']')) { |
| jsonStream->ignore(1); |
| *_value = result; |
| return; |
| } |
| |
| while (true) { |
| Value* currentValue = NULL; |
| parse(jsonStream, ¤tValue); |
| if (!currentValue) { |
| delete result; |
| return; |
| } |
| result->addArrayValue(currentValue); |
| delete currentValue; |
| |
| skipWhitespace(jsonStream); |
| wchar_t nextChar = jsonStream->peek(); |
| if (nextChar == wchar_t(',')) { |
| jsonStream->ignore(1); |
| skipWhitespace(jsonStream); |
| continue; |
| } |
| if (nextChar == wchar_t(']')) { |
| jsonStream->ignore(1); |
| *_value = result; |
| } else { |
| Logger::error("JSON string has an array without expected closing ']'"); |
| delete result; |
| } |
| return; |
| } |
| } |
| |
| double JSONParser::parseNumber(std::wstringstream* jsonStream) { |
| int count = 0; |
| bool looking = true; |
| bool nonZeroEncountered = false; |
| wchar_t currentChar = jsonStream->peek(); |
| while (looking) { |
| if (jsonStream->eof()) { |
| looking = false; |
| break; |
| } |
| switch (currentChar) { |
| case wchar_t('1'): |
| case wchar_t('2'): |
| case wchar_t('3'): |
| case wchar_t('4'): |
| case wchar_t('5'): |
| case wchar_t('6'): |
| case wchar_t('7'): |
| case wchar_t('8'): |
| case wchar_t('9'): |
| case wchar_t('-'): |
| case wchar_t('+'): |
| case wchar_t('e'): |
| case wchar_t('E'): { |
| nonZeroEncountered = true; |
| } |
| // FALL THROUGH |
| case wchar_t('0'): |
| case wchar_t('.'): { |
| jsonStream->ignore(1); |
| count++; |
| break; |
| } |
| default: { |
| looking = false; |
| break; |
| } |
| } |
| currentChar = jsonStream->peek(); |
| } |
| for (int i = 0; i < count; i++) { |
| jsonStream->unget(); |
| } |
| |
| wchar_t* numberChars = new wchar_t[count + 1]; |
| jsonStream->read(numberChars, count); |
| numberChars[count] = wchar_t('\0'); |
| wchar_t* endPoint; |
| double numberValue = wcstod(numberChars, &endPoint); |
| delete[] numberChars; |
| if (numberValue == 0 && nonZeroEncountered) { |
| return -DBL_MAX; |
| } |
| return numberValue; |
| } |
| |
| void JSONParser::parseObject(std::wstringstream* jsonStream, Value** _value) { |
| *_value = NULL; |
| |
| Value* result = new Value(); |
| result->setType(TYPE_OBJECT); |
| |
| jsonStream->ignore(1); |
| skipWhitespace(jsonStream); |
| if (jsonStream->peek() == wchar_t('}')) { |
| jsonStream->ignore(1); |
| *_value = result; |
| return; |
| } |
| |
| while (true) { |
| if (jsonStream->peek() != wchar_t('\"')) { |
| Logger::error("JSON string has an object with a non-String key value"); |
| delete result; |
| return; |
| } |
| std::wstring* key = NULL; |
| parseString(jsonStream, &key); |
| skipWhitespace(jsonStream); |
| if (jsonStream->get() != wchar_t(':')) { |
| Logger::error("JSON string has an object without a ':' separating a key from its _value"); |
| delete key; |
| delete result; |
| return; |
| } |
| Value* assocValue = NULL; |
| skipWhitespace(jsonStream); |
| parse(jsonStream, &assocValue); |
| if (!assocValue) { |
| delete key; |
| delete result; |
| return; |
| } |
| bool success = result->addObjectValue(key, assocValue); |
| delete assocValue; |
| delete key; |
| if (!success) { |
| Logger::error("JSON string has an object with a duplicate key"); |
| delete result; |
| return; |
| } |
| |
| skipWhitespace(jsonStream); |
| wchar_t nextChar = jsonStream->peek(); |
| if (nextChar == wchar_t(',')) { |
| jsonStream->ignore(1); |
| skipWhitespace(jsonStream); |
| continue; |
| } |
| if (nextChar == wchar_t('}')) { |
| jsonStream->ignore(1); |
| *_value = result; |
| } else { |
| Logger::error("JSON string has an object without an expected closing '}'"); |
| delete result; |
| } |
| return; |
| } |
| } |
| |
| void JSONParser::parseString(std::wstringstream* jsonStream, std::wstring** _value) { |
| *_value = NULL; |
| |
| std::wstring* result = new std::wstring; |
| jsonStream->ignore(1); |
| const wchar_t charBackslash = wchar_t('\\'); |
| wchar_t charQuote = wchar_t('\"'); |
| wchar_t currentChar = NULL; |
| jsonStream->read(¤tChar, 1); |
| while (currentChar != charQuote) { |
| if (jsonStream->eof()) { |
| Logger::error("JSON string has string value that does not end"); |
| return; |
| } |
| if (currentChar == charBackslash) { |
| jsonStream->read(¤tChar, 1); |
| if (jsonStream->eof()) { |
| Logger::error("JSON string has string value that does not end"); |
| return; |
| } |
| switch (currentChar) { |
| case wchar_t('\"'): |
| case wchar_t('/'): |
| case charBackslash: { |
| result->push_back(currentChar); |
| break; |
| } |
| case wchar_t('b'): { |
| result->push_back(wchar_t('\b')); |
| break; |
| } |
| case wchar_t('f'): { |
| result->push_back(wchar_t('\f')); |
| break; |
| } |
| case wchar_t('n'): { |
| result->push_back(wchar_t('\n')); |
| break; |
| } |
| case wchar_t('r'): { |
| result->push_back(wchar_t('\r')); |
| break; |
| } |
| case wchar_t('t'): { |
| result->push_back(wchar_t('\t')); |
| break; |
| } |
| case wchar_t('u'): { |
| Logger::log("FYI: unicode value encountered, not parsed, still //TODO"); |
| // TODO |
| break; |
| } |
| default: { |
| Logger::error("JSON string has string value with an invalid escape sequence"); |
| return; |
| } |
| } |
| } else { |
| result->push_back(currentChar); |
| } |
| jsonStream->read(¤tChar, 1); |
| } |
| |
| *_value = result; |
| } |
| |
| void JSONParser::skipWhitespace(std::wstringstream* jsonStream) { |
| wchar_t current = jsonStream->peek(); |
| while (current == wchar_t(' ') || current == wchar_t('\r') || current == wchar_t('\n') || current == wchar_t('\t')) { |
| jsonStream->ignore(1); |
| current = jsonStream->peek(); |
| } |
| } |
| |
| void JSONParser::stringify(Value* value, std::wstring** _jsonString) { |
| switch (value->getType()) { |
| case TYPE_NULL: { |
| std::wstring* result = new std::wstring; |
| result->assign(VALUE_NULL); |
| *_jsonString = result; |
| break; |
| } |
| case TYPE_BOOLEAN: { |
| std::wstring* result = new std::wstring; |
| result->assign(value->getBooleanValue() ? VALUE_TRUE : VALUE_FALSE); |
| *_jsonString = result; |
| break; |
| } |
| case TYPE_NUMBER: { |
| std::wstring* result = new std::wstring; |
| std::wstringstream stringStream; |
| stringStream << value->getNumberValue(); |
| result->assign(stringStream.str()); |
| *_jsonString = result; |
| break; |
| } |
| case TYPE_STRING: { |
| static const wchar_t char_quote('\"'); |
| static const wchar_t char_backslash('\\'); |
| static const wchar_t char_forwardslash('/'); |
| static const wchar_t char_backspace('\b'); |
| static const wchar_t char_formfeed('\f'); |
| static const wchar_t char_newline('\n'); |
| static const wchar_t char_cr('\r'); |
| static const wchar_t char_tab('\t'); |
| |
| std::wstring* source = value->getStringValue(); |
| size_t length = source->length(); |
| wchar_t* chars = (wchar_t*)source->c_str(); |
| |
| std::wstringstream stringStream; |
| stringStream << char_quote; |
| for (size_t i = 0; i < length; i++) { |
| switch (chars[i]) { |
| case char_quote: |
| case char_backslash: |
| case char_forwardslash: { |
| stringStream << char_backslash; |
| stringStream << chars[i]; |
| break; |
| } |
| case char_backspace: { |
| stringStream << "\\b"; |
| break; |
| } |
| case char_formfeed: { |
| stringStream << "\\f"; |
| break; |
| } |
| case char_newline: { |
| stringStream << "\\n"; |
| break; |
| } |
| case char_cr: { |
| stringStream << "\\r"; |
| break; |
| } |
| case char_tab: { |
| stringStream << "\\t"; |
| break; |
| } |
| default: { |
| stringStream << chars[i]; |
| break; |
| } |
| } |
| } |
| stringStream << char_quote; |
| |
| std::wstring* result = new std::wstring; |
| result->assign(stringStream.str()); |
| *_jsonString = result; |
| break; |
| } |
| case TYPE_ARRAY: { |
| Value** arrayValues = NULL; |
| value->getArrayValues(&arrayValues); |
| std::wstring* result = new std::wstring; |
| result->push_back(wchar_t('[')); |
| int index = 0; |
| Value* currentValue = arrayValues[index]; |
| while (currentValue) { |
| std::wstring* serializedValue = NULL; |
| stringify(currentValue, &serializedValue); |
| result->append(*serializedValue); |
| result->push_back(wchar_t(',')); |
| delete serializedValue; |
| currentValue = arrayValues[++index]; |
| } |
| delete[] arrayValues; |
| |
| if (index > 0) { |
| result->erase(result->end() - 1); |
| } |
| result->push_back(wchar_t(']')); |
| *_jsonString = result; |
| break; |
| } |
| case TYPE_OBJECT: { |
| std::wstring** objectKeys = NULL; |
| Value** objectValues = NULL; |
| value->getObjectValues(&objectKeys, &objectValues); |
| std::wstring* result = new std::wstring; |
| result->push_back(wchar_t('{')); |
| int index = 0; |
| std::wstring* currentKey = objectKeys[index]; |
| while (currentKey) { |
| result->push_back(wchar_t('\"')); |
| result->append(*currentKey); |
| result->push_back(wchar_t('\"')); |
| result->push_back(wchar_t(':')); |
| std::wstring* serializedValue = NULL; |
| stringify(objectValues[index], &serializedValue); |
| result->append(*serializedValue); |
| result->push_back(wchar_t(',')); |
| delete serializedValue; |
| currentKey = objectKeys[++index]; |
| } |
| delete[] objectValues; |
| delete[] objectKeys; |
| |
| if (index > 0) { |
| result->erase(result->end() - 1); |
| } |
| result->push_back(wchar_t('}')); |
| *_jsonString = result; |
| break; |
| } |
| default: { |
| *_jsonString = NULL; |
| break; |
| } |
| } |
| } |