diff options
author | MJPA <git@mjpa.co.uk> | 2010-09-23 00:13:02 +0400 |
---|---|---|
committer | MJPA <git@mjpa.co.uk> | 2010-09-23 00:13:02 +0400 |
commit | dc4807e17c85cfed4f0180ae2197cb08cd7f16e6 (patch) | |
tree | 903fed38d0d52d5ab2892189750edfbbb249654d /src | |
parent | baab08ba95dca84bbbce339b33b15b0718e6106d (diff) |
Adding first commit with files
Diffstat (limited to 'src')
-rw-r--r-- | src/JSON.cpp | 263 | ||||
-rw-r--r-- | src/JSON.h | 61 | ||||
-rw-r--r-- | src/JSONValue.cpp | 695 | ||||
-rw-r--r-- | src/JSONValue.h | 79 | ||||
-rw-r--r-- | src/demo/example.cpp | 141 | ||||
-rw-r--r-- | src/demo/functions.h | 11 | ||||
-rw-r--r-- | src/demo/nix-main.cpp | 122 | ||||
-rw-r--r-- | src/demo/testcases.cpp | 118 | ||||
-rw-r--r-- | src/demo/win-main.cpp | 132 |
9 files changed, 1622 insertions, 0 deletions
diff --git a/src/JSON.cpp b/src/JSON.cpp new file mode 100644 index 0000000..2ee24e1 --- /dev/null +++ b/src/JSON.cpp @@ -0,0 +1,263 @@ +/* + * JSON.cpp + * Copyright (C) 2010 Mike Anchor <mikea@mjpa.co.uk> + * + * Part of the MJPA JSON Library - http://mjpa.co.uk/blog/view/A-simple-C-JSON-library/ + * + * License: http://mjpa.co.uk/licenses/GPLv2/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "JSON.h" + +/** + * Blocks off the public constructor + * + * @access private + * + */ +JSON::JSON() +{ +} + +/** + * Parses a complete JSON encoded string + * This is just a wrapper around the UNICODE Parse(). + * + * @access public + * + * @param char* data The JSON text + * + * @return JSONValue* Returns a JSON Value representing the root, or NULL on error + */ +JSONValue *JSON::Parse(const char *data) +{ + size_t length = strlen(data) + 1; + wchar_t *w_data = (wchar_t*)malloc(length * sizeof(wchar_t)); + + #ifdef WIN32 + size_t ret_value = 0; + if (mbstowcs_s(&ret_value, w_data, length, data, length) != 0) + { + free(w_data); + return NULL; + } + #else + if (mbstowcs(w_data, data, length) == (size_t)-1) + { + free(w_data); + return NULL; + } + #endif + + JSONValue *value = JSON::Parse(w_data); + free(w_data); + return value; +} + +/** + * Parses a complete JSON encoded string (UNICODE input version) + * + * @access public + * + * @param wchar_t* data The JSON text + * + * @return JSONValue* Returns a JSON Value representing the root, or NULL on error + */ +JSONValue *JSON::Parse(const wchar_t *data) +{ + // Skip any preceding whitespace, end of data = no JSON = fail + if (!SkipWhitespace(&data)) + return NULL; + + // We need the start of a value here now... + JSONValue *value = JSONValue::Parse(&data); + if (value == NULL) + return NULL; + + // Can be white space now and should be at the end of the string then... + if (SkipWhitespace(&data)) + { + delete value; + return NULL; + } + + // We're now at the end of the string + return value; +} + +/** + * Turns the passed in JSONValue into a JSON encode string + * + * @access public + * + * @param JSONValue* value The root value + * + * @return std::wstring Returns a JSON encoded string representation of the given value + */ +std::wstring JSON::Stringify(JSONValue *value) +{ + if (value != NULL) + return value->Stringify(); + else + return L""; +} + +/** + * Skips over any whitespace characters (space, tab, \r or \n) defined by the JSON spec + * + * @access protected + * + * @param wchar_t** data Pointer to a wchar_t* that contains the JSON text + * + * @return bool Returns true if there is more data, or false if the end of the text was reached + */ +bool JSON::SkipWhitespace(const wchar_t **data) +{ + while (**data != 0 && (**data == L' ' || **data == L'\t' || **data == L'\r' || **data == L'\n')) + (*data)++; + + return **data != 0; +} + +/** + * 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) +{ + size_t str_length = 0; + 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 (wcslen(*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; + } + + // String will be one longer - do it before memory size check + str_length++; + + // Need more memory? + if (str_length > str.capacity()) + { + str_length += 256; + str.reserve(str_length); + } + + // Add the next char + str += next_char; + + // Move on + (*data)++; + } + + // If we're here, the string ended incorrectly + return false; +} + +/** + * Parses some text as though it is an integer + * + * @access protected + * + * @param wchar_t** data Pointer to a wchar_t* that contains the JSON text + * + * @return int Returns the int value of the number found + */ +int JSON::ParseInt(const wchar_t **data) +{ + int integer = 0; + while (**data != 0 && **data >= '0' && **data <= '9') + integer = integer * 10 + (*(*data)++ - '0'); + return integer; +} diff --git a/src/JSON.h b/src/JSON.h new file mode 100644 index 0000000..15dd206 --- /dev/null +++ b/src/JSON.h @@ -0,0 +1,61 @@ +/* + * JSON.h + * Copyright (C) 2010 Mike Anchor <mikea@mjpa.co.uk> + * + * Part of the MJPA JSON Library - http://mjpa.co.uk/blog/view/A-simple-C-JSON-library/ + * + * License: http://mjpa.co.uk/licenses/GPLv2/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _JSON_H_ +#define _JSON_H_ + +// Win32 incompatibilities +#ifdef WIN32 + #define wcsncasecmp _wcsnicmp + static inline bool isnan(double x) { return x != x; } + static inline bool isinf(double x) { return !isnan(x) && isnan(x - x); } +#endif + +#include <vector> +#include <string> +#include <map> + +// Custom types +class JSONValue; +typedef std::vector<JSONValue*> JSONArray; +typedef std::map<std::wstring, JSONValue*> JSONObject; + +#include "JSONValue.h" + +class JSON +{ + friend class JSONValue; + + public: + static JSONValue* Parse(const char *data); + static JSONValue* Parse(const wchar_t *data); + static std::wstring Stringify(JSONValue *value); + protected: + static bool SkipWhitespace(const wchar_t **data); + static bool ExtractString(const wchar_t **data, std::wstring &str); + static int ParseInt(const wchar_t **data); + private: + JSON(); +}; + +#endif diff --git a/src/JSONValue.cpp b/src/JSONValue.cpp new file mode 100644 index 0000000..32501ba --- /dev/null +++ b/src/JSONValue.cpp @@ -0,0 +1,695 @@ +/* + * JSONValue.cpp + * Copyright (C) 2010 Mike Anchor <mikea@mjpa.co.uk> + * + * Part of the MJPA JSON Library - http://mjpa.co.uk/blog/view/A-simple-C-JSON-library/ + * + * License: http://mjpa.co.uk/licenses/GPLv2/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <vector> +#include <string> +#include <sstream> +#include <iostream> +#include <math.h> + +#include "JSONValue.h" + +// Macros to free an array/object +#define FREE_ARRAY(x) { JSONArray::iterator iter; for (iter = x.begin(); iter != x.end(); iter++) { delete *iter; } } +#define FREE_OBJECT(x) { JSONObject::iterator iter; for (iter = x.begin(); iter != x.end(); iter++) { delete (*iter).second; } } + +/** + * 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 ((wcslen(*data) >= 4 && wcsncasecmp(*data, L"true", 4) == 0) || (wcslen(*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 (wcslen(*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 = (double)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 + double decimal = (double)JSON::ParseInt(data); + while((int)decimal > 0) decimal /= 10.0f; + + // 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 + int expo = JSON::ParseInt(data); + for (int i = 0; i < expo; i++) + number = neg_expo ? (number / 10.0) : (number * 10); + } + + // 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; + } +} + +/** + * Basic constructor for creating a JSON Value of type NULL + * + * @access public + */ +JSONValue::JSONValue(/*NULL*/) +{ + type = JSONType_Null; +} + +/** + * Basic constructor for creating a JSON Value of type String + * + * @access public + * + * @param wchar_t* m_char_value The string to use as the value + */ +JSONValue::JSONValue(const wchar_t *m_char_value) +{ + type = JSONType_String; + string_value = std::wstring(m_char_value); +} + +/** + * Basic constructor for creating a JSON Value of type String + * + * @access public + * + * @param std::wstring m_string_value The string to use as the value + */ +JSONValue::JSONValue(std::wstring m_string_value) +{ + type = JSONType_String; + string_value = m_string_value; +} + +/** + * Basic constructor for creating a JSON Value of type Bool + * + * @access public + * + * @param bool m_bool_value The bool to use as the value + */ +JSONValue::JSONValue(bool m_bool_value) +{ + type = JSONType_Bool; + bool_value = m_bool_value; +} + +/** + * Basic constructor for creating a JSON Value of type Number + * + * @access public + * + * @param double m_number_value The number to use as the value + */ +JSONValue::JSONValue(double m_number_value) +{ + type = JSONType_Number; + number_value = m_number_value; +} + +/** + * Basic constructor for creating a JSON Value of type Array + * + * @access public + * + * @param JSONArray m_array_value The JSONArray to use as the value + */ +JSONValue::JSONValue(JSONArray m_array_value) +{ + type = JSONType_Array; + array_value = m_array_value; +} + +/** + * Basic constructor for creating a JSON Value of type Object + * + * @access public + * + * @param JSONObject m_object_value The JSONObject to use as the value + */ +JSONValue::JSONValue(JSONObject m_object_value) +{ + type = JSONType_Object; + object_value = m_object_value; +} + +/** + * The destructor for the JSON Value object + * Handles deleting the objects in the array or the object value + * + * @access public + */ +JSONValue::~JSONValue() +{ + if (type == JSONType_Array) + { + JSONArray::iterator iter; + for (iter = array_value.begin(); iter != array_value.end(); iter++) + delete *iter; + } + else if (type == JSONType_Object) + { + JSONObject::iterator iter; + for (iter = object_value.begin(); iter != object_value.end(); iter++) + { + delete (*iter).second; + } + } +} + +/** + * Checks if the value is a NULL + * + * @access public + * + * @return bool Returns true if it is a NULL value, false otherwise + */ +bool JSONValue::IsNull() +{ + return type == JSONType_Null; +} + +/** + * Checks if the value is a String + * + * @access public + * + * @return bool Returns true if it is a String value, false otherwise + */ +bool JSONValue::IsString() +{ + return type == JSONType_String; +} + +/** + * Checks if the value is a Bool + * + * @access public + * + * @return bool Returns true if it is a Bool value, false otherwise + */ +bool JSONValue::IsBool() +{ + return type == JSONType_Bool; +} + +/** + * Checks if the value is a Number + * + * @access public + * + * @return bool Returns true if it is a Number value, false otherwise + */ +bool JSONValue::IsNumber() +{ + return type == JSONType_Number; +} + +/** + * Checks if the value is an Array + * + * @access public + * + * @return bool Returns true if it is an Array value, false otherwise + */ +bool JSONValue::IsArray() +{ + return type == JSONType_Array; +} + +/** + * Checks if the value is an Object + * + * @access public + * + * @return bool Returns true if it is an Object value, false otherwise + */ +bool JSONValue::IsObject() +{ + return type == JSONType_Object; +} + +/** + * Retrieves the String value of this JSONValue + * Use IsString() before using this method. + * + * @access public + * + * @return std::wstring Returns the string value + */ +std::wstring JSONValue::AsString() +{ + return string_value; +} + +/** + * Retrieves the Bool value of this JSONValue + * Use IsBool() before using this method. + * + * @access public + * + * @return bool Returns the bool value + */ +bool JSONValue::AsBool() +{ + return bool_value; +} + +/** + * Retrieves the Number value of this JSONValue + * Use IsNumber() before using this method. + * + * @access public + * + * @return double Returns the number value + */ +double JSONValue::AsNumber() +{ + return number_value; +} + +/** + * Retrieves the Array value of this JSONValue + * Use IsArray() before using this method. + * + * @access public + * + * @return JSONArray Returns the array value + */ +JSONArray JSONValue::AsArray() +{ + return array_value; +} + +/** + * Retrieves the Object value of this JSONValue + * Use IsObject() before using this method. + * + * @access public + * + * @return JSONObject Returns the object value + */ +JSONObject JSONValue::AsObject() +{ + return object_value; +} + +/** + * Creates a JSON encoded string for the value with all necessary characters escaped + * + * @access public + * + * @return std::wstring Returns the JSON string + */ +std::wstring JSONValue::Stringify() +{ + std::wstring ret_string; + + switch (type) + { + case JSONType_Null: + ret_string = L"null"; + break; + + case JSONType_String: + ret_string = StringifyString(string_value); + break; + + case JSONType_Bool: + ret_string = bool_value ? L"true" : L"false"; + break; + + case JSONType_Number: + { + if (isinf(number_value) || isnan(number_value)) + ret_string = L"null"; + else + { + std::wstringstream ss; + ss << number_value; + ret_string = ss.str(); + } + break; + } + + case JSONType_Array: + { + ret_string = L"["; + JSONArray::iterator iter = array_value.begin(); + while (iter != array_value.end()) + { + ret_string += (*iter)->Stringify(); + + // Not at the end - add a separator + if (++iter != array_value.end()) + ret_string += L","; + } + ret_string += L"]"; + break; + } + + case JSONType_Object: + { + ret_string = L"{"; + JSONObject::iterator iter = object_value.begin(); + while (iter != object_value.end()) + { + ret_string += StringifyString((*iter).first); + ret_string += L":"; + ret_string += (*iter).second->Stringify(); + + // Not at the end - add a separator + if (++iter != object_value.end()) + ret_string += L","; + } + ret_string += L"}"; + break; + } + } + + return ret_string; +} + +/** + * Creates a JSON encoded string with all required fields escaped + * Works from http://www.ecma-internationl.org/publications/files/ECMA-ST/ECMA-262.pdf + * Section 15.12.3. + * + * @access private + * + * @param std::wstring str The string that needs to have the characters escaped + * + * @return std::wstring Returns the JSON string + */ +std::wstring JSONValue::StringifyString(std::wstring str) +{ + std::wstring str_out = L"\""; + + std::wstring::iterator iter = str.begin(); + while (iter != str.end()) + { + wchar_t chr = *iter; + + if (chr == L'"' || chr == L'\\' || chr == L'/') + { + str_out += L'\\'; + str_out += chr; + } + else if (chr == L'\b') + { + str_out += L"\\b"; + } + else if (chr == L'\f') + { + str_out += L"\\f"; + } + else if (chr == L'\n') + { + str_out += L"\\n"; + } + else if (chr == L'\r') + { + str_out += L"\\r"; + } + else if (chr == L'\t') + { + str_out += L"\\t"; + } + else if (chr < L' ') + { + str_out += L"\\u"; + for (int i = 0; i < 4; i++) + { + int value = (chr >> 12) & 0xf; + if (value >= 0 && value <= 9) + str_out += (wchar_t)('0' + value); + else if (value >= 10 && value <= 15) + str_out += (wchar_t)('A' + (value - 10)); + chr <<= 4; + } + } + else + { + str_out += chr; + } + + iter++; + } + + str_out += L"\""; + return str_out; +} diff --git a/src/JSONValue.h b/src/JSONValue.h new file mode 100644 index 0000000..a58f9ee --- /dev/null +++ b/src/JSONValue.h @@ -0,0 +1,79 @@ +/* + * JSONValue.h + * Copyright (C) 2010 Mike Anchor <mikea@mjpa.co.uk> + * + * Part of the MJPA JSON Library - http://mjpa.co.uk/blog/view/A-simple-C-JSON-library/ + * + * License: http://mjpa.co.uk/licenses/GPLv2/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _JSONVALUE_H_ +#define _JSONVALUE_H_ + +#include <vector> +#include <string> + +#include "JSON.h" + +class JSON; + +enum JSONType { JSONType_Null, JSONType_String, JSONType_Bool, JSONType_Number, JSONType_Array, JSONType_Object }; + +class JSONValue +{ + friend class JSON; + + public: + JSONValue(/*NULL*/); + JSONValue(const wchar_t *m_char_value); + JSONValue(std::wstring m_string_value); + JSONValue(bool m_bool_value); + JSONValue(double m_number_value); + JSONValue(JSONArray m_array_value); + JSONValue(JSONObject m_object_value); + ~JSONValue(); + + bool IsNull(); + bool IsString(); + bool IsBool(); + bool IsNumber(); + bool IsArray(); + bool IsObject(); + + std::wstring AsString(); + bool AsBool(); + double AsNumber(); + JSONArray AsArray(); + JSONObject AsObject(); + + std::wstring Stringify(); + + protected: + static JSONValue *Parse(const wchar_t **data); + + private: + static std::wstring StringifyString(std::wstring str); + + JSONType type; + std::wstring string_value; + bool bool_value; + double number_value; + JSONArray array_value; + JSONObject object_value; +}; + +#endif diff --git a/src/demo/example.cpp b/src/demo/example.cpp new file mode 100644 index 0000000..723b730 --- /dev/null +++ b/src/demo/example.cpp @@ -0,0 +1,141 @@ +/* + * example.cpp + * Copyright (C) 2010 Mike Anchor <mikea@mjpa.co.uk> + * + * Part of the MJPA JSON Library Demo - http://mjpa.co.uk/blog/view/A-simple-C-JSON-library/ + * + * License: http://mjpa.co.uk/licenses/GPLv2/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <string> +#include <sstream> +#include <time.h> +#include "../JSON.h" +#include "functions.h" + +using namespace std; + +// Just some sample JSON text, feel free to change but could break demo +wchar_t* EXAMPLE = L"\ +{ \ + \"string_name\" : \"string\tvalue and a \\\"quote\\\" and a unicode char \\u00BE and a c:\\\\path\\\\ or a \\/unix\\/path\\/ :D\", \ + \"bool_name\" : true, \ + \"bool_second\" : FaLsE, \ + \"null_name\" : nULl, \ + \"negative\" : -34.276, \ + \"sub_object\" : { \ + \"foo\" : \"abc\", \ + \"bar\" : 1.35e2, \ + \"blah\" : { \"a\" : \"A\", \"b\" : \"B\", \"c\" : \"C\" } \ + }, \ + \"array_letters\" : [ \"a\", \"b\", \"c\", [ 1, 2, 3 ] ] \ +} "; + +// Example 1 +void example1() +{ + // Parse example data + JSONValue *value = JSON::Parse(EXAMPLE); + + // Did it go wrong? + if (value == NULL) + { + print_out(L"Example code failed to parse, did you change it?\r\n"); + } + else + { + // Retrieve the main object + JSONObject root; + if (value->IsObject() == false) + { + print_out(L"The root element is not an object, did you change the example?\r\n"); + } + else + { + root = value->AsObject(); + + // Retrieving a string + if (root.find(L"string_name") != root.end() && root[L"string_name"]->IsString()) + { + print_out(L"string_name:\r\n"); + print_out(L"------------\r\n"); + print_out(root[L"string_name"]->AsString().c_str()); + print_out(L"\r\n\r\n"); + } + + // Retrieving a boolean + if (root.find(L"bool_second") != root.end() && root[L"bool_second"]->IsBool()) + { + print_out(L"bool_second:\r\n"); + print_out(L"------------\r\n"); + print_out(root[L"bool_second"]->AsBool() ? L"it's true!" : L"it's false!"); + print_out(L"\r\n\r\n"); + } + + // Retrieving an array + if (root.find(L"array_letters") != root.end() && root[L"array_letters"]->IsArray()) + { + JSONArray array = root[L"array_letters"]->AsArray(); + print_out(L"array_letters:\r\n"); + print_out(L"--------------\r\n"); + for (unsigned int i = 0; i < array.size(); i++) + { + wstringstream output; + output << L"[" << i << L"] => " << array[i]->Stringify() << L"\r\n"; + print_out(output.str().c_str()); + } + print_out(L"\r\n"); + } + + // Retrieving nested object + if (root.find(L"sub_object") != root.end() && root[L"sub_object"]->IsObject()) + { + print_out(L"sub_object:\r\n"); + print_out(L"-----------\r\n"); + print_out(root[L"sub_object"]->Stringify().c_str()); + print_out(L"\r\n\r\n"); + } + } + + delete value; + } +} + +// Example 2 +void example2() +{ + JSONObject root; + + // Adding a string + root[L"test_string"] = new JSONValue(L"hello world"); + + // Create a random integer array + JSONArray array; + srand((unsigned)time(0)); + for (int i = 0; i < 10; i++) + array.push_back(new JSONValue((double)(rand() % 100))); + root[L"sample_array"] = new JSONValue(array); + + // Create a value + JSONValue *value = new JSONValue(root); + + // Print it + print_out(value->Stringify().c_str()); + + // Clean up + delete value; +} diff --git a/src/demo/functions.h b/src/demo/functions.h new file mode 100644 index 0000000..82951c1 --- /dev/null +++ b/src/demo/functions.h @@ -0,0 +1,11 @@ +// The functions available, 1 single header to make things easier + +// The print out function +void print_out(const wchar_t *output); + +// Example functions +void example1(); +void example2(); + +// Test case runner +void run_tests(); diff --git a/src/demo/nix-main.cpp b/src/demo/nix-main.cpp new file mode 100644 index 0000000..17809b1 --- /dev/null +++ b/src/demo/nix-main.cpp @@ -0,0 +1,122 @@ +/* + * nix-main.cpp + * Copyright (C) 2010 Mike Anchor <mikea@mjpa.co.uk> + * + * Part of the MJPA JSON Library Demo - http://mjpa.co.uk/blog/view/A-simple-C-JSON-library/ + * + * License: http://mjpa.co.uk/licenses/GPLv2/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <string> +#include <iostream> +#include <iterator> + +#include "../JSON.h" +#include "functions.h" + +using namespace std; + +// Print out function +void print_out(const wchar_t* output) +{ + wcout << output; + wcout.flush(); +} + +// Linux entry point +int main(int argc, char **argv) +{ + // Required for utf8 chars + setlocale(LC_CTYPE, ""); + + // The mode... + string mode = argc != 2 ? "" : argv[1]; + + // Verifying? + if (mode == "-v" || mode == "-f") + { + // Get the stdin data + cin >> noskipws; + istream_iterator<char> it(cin); + istream_iterator<char> end; + string results(it, end); + + // Parse it, print if the test is good/bad + JSONValue *value = JSON::Parse(results.c_str()); + if ((value != NULL && mode == "-v") || (value == NULL && mode == "-f")) + wcout << L"PASS" << endl; + else + wcout << L"FAIL" << endl; + + if (value) delete value; + } + + // Parse + echo? + else if (mode == "-e") + { + // Get the stdin data + cin >> noskipws; + istream_iterator<char> it(cin); + istream_iterator<char> end; + string results(it, end); + + // Parse it & echo if it's valid + JSONValue *value = NULL; + if ((value = JSON::Parse(results.c_str())) == NULL) + wcout << L"Code entered is *NOT* valid."; + else + wcout << value->Stringify(); + wcout << endl; + + if (value) delete value; + } + + // Example 1? + else if (mode == "-ex1") + { + example1(); + } + + // Example 2? + else if (mode == "-ex2") + { + example2(); + } + + // Test cases? + else if (mode == "-t") + { + run_tests(); + } + + // Help! + else + { + wcout << L"Usage: " << argv[0] << L" <option>" << endl; + wcout << endl; + wcout << L"\t-v\tVerify JSON string is *valid* via stdin" << endl; + wcout << L"\t-f\tVerify JSON string is *invalid* via stdin" << endl; + wcout << L"\t-e\tVerify JSON string via stdin and echo it back using Stringify()" << endl; + wcout << L"\t-ex1\tRun example 1 - Example of how to extract data from the JSONValue object" << endl; + wcout << L"\t-ex2\tRun example 2 - Building a JSONValue from nothing" << endl; + wcout << L"\t-t\tRun test cases" << endl; + wcout << endl; + wcout << L"Only one option can be used at a time." << endl; + } + + return 0; +} diff --git a/src/demo/testcases.cpp b/src/demo/testcases.cpp new file mode 100644 index 0000000..ff0055d --- /dev/null +++ b/src/demo/testcases.cpp @@ -0,0 +1,118 @@ +/* + * testcases.cpp + * Copyright (C) 2010 Mike Anchor <mikea@mjpa.co.uk> + * + * Part of the MJPA JSON Library Demo - http://mjpa.co.uk/blog/view/A-simple-C-JSON-library/ + * + * License: http://mjpa.co.uk/licenses/GPLv2/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * Test cases converted from http://testsuites.opera.com/JSON/runner.htm + */ + +#include <string> +#include <sstream> +#include <fstream> +#include <iostream> +#include <iomanip> +#include "../JSON.h" +#include "functions.h" + +// Set to the width of the description column +#define DESC_LENGTH 50 + +using namespace std; + +// Helper to do a quick parse check +bool parse_check(wstring str) +{ + JSONValue *v = JSON::Parse(str.c_str()); + if (v) + { + delete v; + return true; + } + else + return false; +} + +// Helper to get a files contents +bool get_file(string filename, wstring &description, wstring &data) +{ + wifstream in(filename.c_str()); + if (in.is_open() == false) + return false; + + getline(in, description); + if (description.length() > DESC_LENGTH) + description.resize(DESC_LENGTH); + + wstring line; + data = L""; + while (getline(in, line)) + { + data += line; + if (!in.eof()) data += L"\n"; + } + return true; +} + +// Run a pass / fail test +void run_test_type(bool type) +{ + int test = 0; + wstring data = L"", name = L""; + ostringstream stream; + wostringstream wstream; + + while (true) + { + stream.str(""); + stream << "test_cases/" << (type ? "pass" : "fail") << (++test) << ".json"; + if (get_file(stream.str(), name, data) == false) break; + + print_out(L"| "); + + wstream.str(L""); + wstream.setf(ios_base::left, ios_base::adjustfield); + wstream << setw(DESC_LENGTH) << name; + print_out(wstream.str().c_str()); + + print_out(L" | "); + print_out(parse_check(data) != type ? L"failed" : L"passed"); + print_out(L" |\r\n"); + } +} + +// Tests to run +void run_tests() +{ + wstring vert_sep = wstring(L"+-") + wstring(DESC_LENGTH, L'-') + wstring(L"-+--------+\r\n"); + + print_out(vert_sep.c_str()); + + wstring header = wstring(L"| Test case") + wstring(DESC_LENGTH - 9, L' ') + wstring(L" | Result |\r\n"); + print_out(header.c_str()); + + print_out(vert_sep.c_str()); + + run_test_type(true); + run_test_type(false); + + print_out(vert_sep.c_str()); +} diff --git a/src/demo/win-main.cpp b/src/demo/win-main.cpp new file mode 100644 index 0000000..7463b42 --- /dev/null +++ b/src/demo/win-main.cpp @@ -0,0 +1,132 @@ +/* + * win-main.cpp + * Copyright (C) 2010 Mike Anchor <mikea@mjpa.co.uk> + * + * Part of the MJPA JSON Library Demo - http://mjpa.co.uk/blog/view/A-simple-C-JSON-library/ + * + * License: http://mjpa.co.uk/licenses/GPLv2/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <windows.h> +#include "..\..\vs2008\resource.h" +#include "..\JSON.h" +#include "functions.h" + +// Handle to the window +static HWND hDlg = NULL; + +// A function to display output +void print_out(const wchar_t *output) +{ + if (hDlg != NULL) + { + size_t size = GetWindowTextLength(GetDlgItem(hDlg, IDC_OUTPUT)) + 1 + wcslen(output); + wchar_t *current = new wchar_t[size]; + GetWindowText(GetDlgItem(hDlg, IDC_OUTPUT), current, size); + wcscat_s(current, size, output); + SetWindowText(GetDlgItem(hDlg, IDC_OUTPUT), current); + } +} + +// Dialog proc for the Windows window +int CALLBACK DialogProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) + { + case WM_INITDIALOG: + hDlg = hWnd; + break; + + case WM_CLOSE: + EndDialog(hWnd, 0); + break; + + case WM_DESTROY: + PostQuitMessage(0); + break; + + case WM_COMMAND: + { + if (HIWORD(wParam) != BN_CLICKED) + break; + + SetWindowText(GetDlgItem(hWnd, IDC_OUTPUT), L""); + + switch (LOWORD(wParam)) + { + case IDC_VERIFY: + case IDC_ECHO: + { + int size = GetWindowTextLength(GetDlgItem(hWnd, IDC_INPUT)); + wchar_t* text = new wchar_t[size + 1]; + GetWindowText(GetDlgItem(hWnd, IDC_INPUT), text, size + 1); + + JSONValue *value = JSON::Parse(text); + + if (value == NULL) + { + SetWindowText(GetDlgItem(hWnd, IDC_OUTPUT), L"The JSON text *is not* valid"); + } + else + { + if (LOWORD(wParam) == IDC_ECHO) + SetWindowText(GetDlgItem(hWnd, IDC_OUTPUT), value->Stringify().c_str()); + else + SetWindowText(GetDlgItem(hWnd, IDC_OUTPUT), L"The JSON text *is* valid"); + delete value; + } + + delete text; + break; + } + + case IDC_EX1: + extern wchar_t *EXAMPLE; + SetWindowText(GetDlgItem(hDlg, IDC_INPUT), EXAMPLE); + example1(); + break; + + case IDC_EX2: + SetWindowText(GetDlgItem(hDlg, IDC_INPUT), L"Example 2:\r\n\r\nKey 'test_string' has a value 'hello world'\r\nKey 'sample_array' is an array of 10 random numbers"); + example2(); + break; + + case IDC_TESTCASES: + SetWindowText(GetDlgItem(hWnd, IDC_INPUT), L""); + run_tests(); + break; + } + + break; + } + } + + return 0; +} + +// Windows entry point +int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) +{ + wchar_t progdir[_MAX_PATH + 1]; + progdir[GetModuleFileName(hInstance, progdir, _MAX_PATH + 1)] = 0; + wchar_t *last_slash = wcsrchr(progdir, L'\\'); + if (last_slash) *(++last_slash) = 0; + SetCurrentDirectory(progdir); + + DialogBox(hInstance, MAKEINTRESOURCE(IDD_JSONDEMO), NULL, DialogProc); + return 0; +} |