// Win32 incompatibilities
#if defined(WIN32) && !defined(__GNUC__)
#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>
// Linux compile fix - from quaker66
#ifdef __GNUC__
#include <cstring>
#include <cstdlib>
#endif
// Mac compile fixes - from quaker66, Lion fix by dabrahams
#if defined(__APPLE__) && __DARWIN_C_LEVEL < 200809L || (defined(WIN32) && defined(__GNUC__)) || defined(ANDROID)
#include <wctype.h>
#include <wchar.h>
static inline int wcsncasecmp(const wchar_t *s1, const wchar_t *s2, size_t n)
{
int lc1 = 0;
int lc2 = 0;
while (n--)
{
lc1 = towlower (*s1);
lc2 = towlower (*s2);
if (lc1 != lc2)
return (lc1 - lc2);
if (!lc1)
return 0;
++s1;
++s2;
}
return 0;
}
#endif
// Simple function to check a string 's' has at least 'n' characters
static inline bool simplejson_wcsnlen(const wchar_t *s, size_t n) {
if (s == 0)
return false;
const wchar_t *save = s;
while (n-- > 0)
{
if (*(save++) == 0) return false;
}
return true;
}
// Custom types
class JSONValue;
typedef std::vector<JSONValue*> JSONArray;
typedef std::map<std::wstring, JSONValue*> JSONObject;
#include <vector>
#include <string>
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(const std::wstring &m_string_value);
JSONValue(bool m_bool_value);
JSONValue(double m_number_value);
JSONValue(const JSONArray &m_array_value);
JSONValue(const JSONObject &m_object_value);
~JSONValue();
bool IsNull() const;
bool IsString() const;
bool IsBool() const;
bool IsNumber() const;
bool IsArray() const;
bool IsObject() const;
const std::wstring &AsString() const;
bool AsBool() const;
double AsNumber() const;
const JSONArray &AsArray() const;
const JSONObject &AsObject() const;
std::size_t CountChildren() const;
bool HasChild(std::size_t index) const;
JSONValue *Child(std::size_t index);
bool HasChild(const wchar_t* name) const;
JSONValue *Child(const wchar_t* name);
std::wstring Stringify() const;
protected:
static JSONValue *Parse(const wchar_t **data);
private:
static std::wstring StringifyString(const std::wstring &str);
JSONType type;
std::wstring string_value;
bool bool_value;
double number_value;
JSONArray array_value;
JSONObject object_value;
};
class JSON
{
friend class JSONValue;
public:
static JSONValue* Parse(const char *data);
static JSONValue* Parse(const wchar_t *data);
static std::wstring Stringify(const JSONValue *value);
protected:
static bool SkipWhitespace(const wchar_t **data);
static bool ExtractString(const wchar_t **data, std::wstring &str);
static double ParseInt(const wchar_t **data);
static double ParseDecimal(const wchar_t **data);
private:
JSON();
};
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <vector>
#include <string>
#include <sstream>
#include <iostream>
#include <math.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 ((simplejson_wcsnlen(*data, 4) && wcsncasecmp(*data, L"true", 4) == 0) || (simplejson_wcsnlen(*data, 5) && wcsncasecmp(*data, L"false", 5) == 0))
{
bool value = wcsncasecmp(*data, L"true", 4) == 0;
(*data) += value ? 4 : 5;
return new JSONValue(value);
}
// Is it a null?
else if (simplejson_wcsnlen(*data, 4) && wcsncasecmp(*data, L"null", 4) == 0)
{
(*data) += 4;
return new JSONValue();
}
// Is it a number?
else if (**data == L'-' || (**data >= L'0' && **data <= L'9'))
{
// Negative?
bool neg = **data == L'-';
if (neg) (*data)++;
double number = 0.0;
// Parse the whole part of the number - only if it wasn't 0
if (**data == L'0')
(*data)++;
else if (**data >= L'1' && **data <= L'9')
number = JSON::ParseInt(data);
else
return NULL;
// Could be a decimal now...
if (**data == '.')
{
(*data)++;
// Not get any digits?
if (!(**data >= L'0' && **data <= L'9'))
return NULL;
// Find the decimal and sort the decimal place out
// Use ParseDecimal as ParseInt won't work with decimals less than 0.1
// thanks to Javier Abadia for the report & fix
double decimal = JSON::ParseDecimal(data);
// Save the number
number += decimal;
}
// Could be an exponent now...
if (**data == L'E' || **data == L'e')
{
(*data)++;
// Check signage of expo
bool neg_expo = false;
if (**data == L'-' || **data == L'+')
{
neg_expo = **data == L'-';
(*data)++;
}
// Not get any digits?
if (!(**data >= L'0' && **data <= L'9'))
return NULL;
// Sort the expo out
double expo = JSON::ParseInt(data);
for (double i = 0.0; i < expo; i++)
number = neg_expo ? (number / 10.0) : (number * 10.0);
}
// Was it neg?
if (neg) number *= -1;
return new JSONValue(number);
}
// An object?
else if (**data == L'{')
{
JSONObject object;
(*data)++;
while (**data != 0)
{
// Whitespace at the start?
if (!JSON::SkipWhitespace(data))
{
FREE_OBJECT(object);
return NULL;
}
// Special case - empty object
if (object.size() == 0 && **data == L'}')
{
(*data)++;
return new JSONValue(object);
}
// We want a string now...
std::wstring name;
if (!JSON::ExtractString(&(++(*data)), name))
{
FREE_OBJECT(object);
return NULL;
}
// More whitespace?
if (!JSON::SkipWhitespace(data))
{
FREE_OBJECT(object);
return NULL;
}
// Need a : now
if (*((*data)++) != L':')
{
FREE_OBJECT(object);
return NULL;
}
// More whitespace?
if (!JSON::SkipWhitespace(data))
{
FREE_OBJECT(object);
return NULL;
}
// The value is here
JSONValue *value = Parse(data);
if (value == NULL)
{
FREE_OBJECT(object);
return NULL;
}
// Add the name:value
if (object.find(name) != object.end())
delete object[name];
object[name] = value;
// More whitespace?
if (!JSON::SkipWhitespace(data))
{
FREE_OBJECT(object);
return NULL;
}
// End of object?
if (**data == L'}')
{
(*data)++;
return new JSONValue(object);
}
// Want a , now
if (**data != L',')
{
FREE_OBJECT(object);
return NULL;
}
(*data)++;
}
// Only here if we ran out of data
FREE_OBJECT(object);
return NULL;
}
// An array?
else if (**data == L'[')
{
JSONArray array;
(*data)++;
while (**data != 0)
{
// Whitespace at the start?
if (!JSON::SkipWhitespace(data))
{
FREE_ARRAY(array);
return NULL;
}
// Special case - empty array
if (array.size() == 0 && **data == L']')
{
(*data)++;
return new JSONValue(array);
}
// Get the value
JSONValue *value = Parse(data);
if (value == NULL)
{
FREE_ARRAY(array);
return NULL;
}
// Add the value
array.push_back(value);
// More whitespace?
if (!JSON::SkipWhitespace(data))
{
FREE_ARRAY(array);
return NULL;
}
// End of array?
if (**data == L']')
{
(*data)++;
return new JSONValue(array);
}
// Want a , now
if (**data != L',')
{
FREE_ARRAY(array);
return NULL;
}
(*data)++;
}
// Only here if we ran out of data
FREE_ARRAY(array);
return NULL;
}
// Ran out of possibilites, it's bad!
else
{
return NULL;
}
}
/**
* 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(const 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(const 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(const 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() const
{
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() const
{
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() const
{
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() const
{
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() const
{
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() const
{
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
*/
const std::wstring &JSONValue::AsString() const
{
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() const
{
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() const
{
return number_value;
}
/**
* Retrieves the Array value of this JSONValue
* Use IsArray() before using this method.
*
* @access public
*
* @return JSONArray Returns the array value
*/
const JSONArray &JSONValue::AsArray() const
{
return array_value;
}
/**
* Retrieves the Object value of this JSONValue
* Use IsObject() before using this method.
*
* @access public
*
* @return JSONObject Returns the object value
*/
const JSONObject &JSONValue::AsObject() const
{
return object_value;
}
/**
* Retrieves the number of children of this JSONValue.
* This number will be 0 or the actual number of children
* if IsArray() or IsObject().
*
* @access public
*
* @return The number of children.
*/
std::size_t JSONValue::CountChildren() const
{
switch (type)
{
case JSONType_Array:
return array_value.size();
case JSONType_Object:
return object_value.size();
default:
return 0;
}
}
/**
* Checks if this JSONValue has a child at the given index.
* Use IsArray() before using this method.
*
* @access public
*
* @return bool Returns true if the array has a value at the given index.
*/
bool JSONValue::HasChild(std::size_t index) const
{
if (type == JSONType_Array)
{
return index < array_value.size();
}
else
{
return false;
}
}
/**
* Retrieves the child of this JSONValue at the given index.
* Use IsArray() before using this method.
*
* @access public
*
* @return JSONValue* Returns JSONValue at the given index or NULL
* if it doesn't exist.
*/
JSONValue *JSONValue::Child(std::size_t index)
{
if (index < array_value.size())
{
return array_value[index];
}
else
{
return NULL;
}
}
/**
* Checks if this JSONValue has a child at the given key.
* Use IsObject() before using this method.
*
* @access public
*
* @return bool Returns true if the object has a value at the given key.
*/
bool JSONValue::HasChild(const wchar_t* name) const
{
if (type == JSONType_Object)
{
return object_value.find(name) != object_value.end();
}
else
{
return false;
}
}
/**
* Retrieves the child of this JSONValue at the given key.
* Use IsObject() before using this method.
*
* @access public
*
* @return JSONValue* Returns JSONValue for the given key in the object
* or NULL if it doesn't exist.
*/
JSONValue* JSONValue::Child(const wchar_t* name)
{
JSONObject::const_iterator it = object_value.find(name);
if (it != object_value.end())
{
return it->second;
}
else
{
return NULL;
}
}
/**
* 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() const
{
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.precision(15);
ss << number_value;
ret_string = ss.str();
}
break;
}
case JSONType_Array:
{
ret_string = L"[";
JSONArray::const_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::const_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://w...content-available-to-author-only...l.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(const std::wstring &str)
{
std::wstring str_out = L"\"";
std::wstring::const_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;
}
/**
* 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));
#if defined(WIN32) && !defined(__GNUC__)
size_t ret_value = 0;
if (mbstowcs_s(&ret_value, w_data, length, data, length) != 0)
{
free(w_data);
return NULL;
}
#elif defined(ANDROID)
// mbstowcs seems to misbehave on android
for(size_t i = 0; i<length; i++)
w_data[i] = (wchar_t)data[i];
#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(const 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)
{
str = L"";
while (**data != 0)
{
// Save the char so we can change it if need be
wchar_t next_char = **data;
// Escaping something?
if (next_char == L'\\')
{
// Move over the escape char
(*data)++;
// Deal with the escaped char
switch (**data)
{
case L'"': next_char = L'"'; break;
case L'\\': next_char = L'\\'; break;
case L'/': next_char = L'/'; break;
case L'b': next_char = L'\b'; break;
case L'f': next_char = L'\f'; break;
case L'n': next_char = L'\n'; break;
case L'r': next_char = L'\r'; break;
case L't': next_char = L'\t'; break;
case L'u':
{
// We need 5 chars (4 hex + the 'u') or its not valid
if (!simplejson_wcsnlen(*data, 5))
return false;
// Deal with the chars
next_char = 0;
for (int i = 0; i < 4; i++)
{
// Do it first to move off the 'u' and leave us on the
// final hex digit as we move on by one later on
(*data)++;
next_char <<= 4;
// Parse the hex digit
if (**data >= '0' && **data <= '9')
next_char |= (**data - '0');
else if (**data >= 'A' && **data <= 'F')
next_char |= (10 + (**data - 'A'));
else if (**data >= 'a' && **data <= 'f')
next_char |= (10 + (**data - 'a'));
else
{
// Invalid hex digit = invalid JSON
return false;
}
}
break;
}
// By the spec, only the above cases are allowed
default:
return false;
}
}
// End of the string?
else if (next_char == L'"')
{
(*data)++;
str.reserve(); // Remove unused capacity
return true;
}
// Disallowed char?
else if (next_char < L' ' && next_char != L'\t')
{
// SPEC Violation: Allow tabs due to real world cases
return false;
}
// Add the next char
str += next_char;
// Move on
(*data)++;
}
// If we're here, the string ended incorrectly
return false;
}
/**
* Parses 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 double Returns the double value of the number found
*/
double JSON::ParseInt(const wchar_t **data)
{
double integer = 0;
while (**data != 0 && **data >= '0' && **data <= '9')
integer = integer * 10 + (*(*data)++ - '0');
return integer;
}
/**
* Parses some text as though it is a decimal
*
* @access protected
*
* @param wchar_t** data Pointer to a wchar_t* that contains the JSON text
*
* @return double Returns the double value of the decimal found
*/
double JSON::ParseDecimal(const wchar_t **data)
{
double decimal = 0.0;
double factor = 0.1;
while (**data != 0 && **data >= '0' && **data <= '9')
{
int digit = (*(*data)++ - '0');
decimal = decimal + digit * factor;
factor *= 0.1;
}
return decimal;
}
int main() {
char *json_string = "[[{\"x\":36},{\"y\":0},{\"value\":0.47873455286026}], \
[{\"x\":68},{\"y\":0},{\"value\":0.00944233685731888}], \
[{\"x\":35},{\"y\":0},{\"value\":0.00944233685731888}], \
[{\"x\":206},{\"y\":0},{\"value\":0.00944233685731888}]]";
JSONValue *data = JSON::Parse(json_string);
for( size_t i=0; i<data->CountChildren(); ++i ) {
JSONValue *row = data->Child(i);
std::cout << "x = " << row->Child(size_t(0))->Child(L"x")->AsNumber() << std::endl;
std::cout << "y = " << row->Child(size_t(1))->Child(L"y")->AsNumber() << std::endl;
std::cout << "value = " << row->Child(size_t(2))->Child(L"value")->AsNumber() << std::endl;
std::cout << std::endl;
}
return 0;
}