Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/MJPA/SimpleJSON.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMJPA <git@mjpa.co.uk>2010-09-23 00:13:02 +0400
committerMJPA <git@mjpa.co.uk>2010-09-23 00:13:02 +0400
commitdc4807e17c85cfed4f0180ae2197cb08cd7f16e6 (patch)
tree903fed38d0d52d5ab2892189750edfbbb249654d /src
parentbaab08ba95dca84bbbce339b33b15b0718e6106d (diff)
Adding first commit with files
Diffstat (limited to 'src')
-rw-r--r--src/JSON.cpp263
-rw-r--r--src/JSON.h61
-rw-r--r--src/JSONValue.cpp695
-rw-r--r--src/JSONValue.h79
-rw-r--r--src/demo/example.cpp141
-rw-r--r--src/demo/functions.h11
-rw-r--r--src/demo/nix-main.cpp122
-rw-r--r--src/demo/testcases.cpp118
-rw-r--r--src/demo/win-main.cpp132
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;
+}