updated SuperEasyJSON

git-svn-id: http://svn.code.sf.net/p/retroshare/code/trunk@8085 b45a01b8-16f6-495d-af2f-9b41ad6348cc
This commit is contained in:
electron128 2015-03-27 18:43:07 +00:00
parent d65adeb2ea
commit 336001abd9
2 changed files with 377 additions and 56 deletions

View File

@ -1,3 +1,5 @@
#define nullptr 0
#include "json.h" #include "json.h"
#include <stdlib.h> #include <stdlib.h>
#include <string> #include <string>
@ -9,6 +11,7 @@
#include <functional> #include <functional>
#include <cctype> #include <cctype>
#include <stack> #include <stack>
#include <cerrno>
#ifndef WIN32 #ifndef WIN32
#define _stricmp strcasecmp #define _stricmp strcasecmp
@ -108,37 +111,49 @@ Value& Value::operator =(const Value& v)
Value& Value::operator [](size_t idx) Value& Value::operator [](size_t idx)
{ {
assert(mValueType == ArrayVal); if (mValueType != ArrayVal)
throw std::runtime_error("json mValueType==ArrayVal required");
return mArrayVal[idx]; return mArrayVal[idx];
} }
const Value& Value::operator [](size_t idx) const const Value& Value::operator [](size_t idx) const
{ {
assert(mValueType == ArrayVal); if (mValueType != ArrayVal)
throw std::runtime_error("json mValueType==ArrayVal required");
return mArrayVal[idx]; return mArrayVal[idx];
} }
Value& Value::operator [](const std::string& key) Value& Value::operator [](const std::string& key)
{ {
assert(mValueType == ObjectVal); if (mValueType != ObjectVal)
throw std::runtime_error("json mValueType==ObjectVal required");
return mObjectVal[key]; return mObjectVal[key];
} }
Value& Value::operator [](const char* key) Value& Value::operator [](const char* key)
{ {
assert(mValueType == ObjectVal); if (mValueType != ObjectVal)
throw std::runtime_error("json mValueType==ObjectVal required");
return mObjectVal[key]; return mObjectVal[key];
} }
const Value& Value::operator [](const char* key) const const Value& Value::operator [](const char* key) const
{ {
assert(mValueType == ObjectVal); if (mValueType != ObjectVal)
throw std::runtime_error("json mValueType==ObjectVal required");
return mObjectVal[key]; return mObjectVal[key];
} }
const Value& Value::operator [](const std::string& key) const const Value& Value::operator [](const std::string& key) const
{ {
assert(mValueType == ObjectVal); if (mValueType != ObjectVal)
throw std::runtime_error("json mValueType==ObjectVal required");
return mObjectVal[key]; return mObjectVal[key];
} }
@ -157,22 +172,140 @@ size_t Value::size() const
bool Value::HasKey(const std::string &key) const bool Value::HasKey(const std::string &key) const
{ {
assert(mValueType == ObjectVal); if (mValueType != ObjectVal)
throw std::runtime_error("json mValueType==ObjectVal required");
return mObjectVal.HasKey(key); return mObjectVal.HasKey(key);
} }
int Value::HasKeys(const std::vector<std::string> &keys) const int Value::HasKeys(const std::vector<std::string> &keys) const
{ {
assert(mValueType == ObjectVal); if (mValueType != ObjectVal)
throw std::runtime_error("json mValueType==ObjectVal required");
return mObjectVal.HasKeys(keys); return mObjectVal.HasKeys(keys);
} }
int Value::HasKeys(const char **keys, int key_count) const int Value::HasKeys(const char **keys, int key_count) const
{ {
assert(mValueType == ObjectVal); if (mValueType != ObjectVal)
throw std::runtime_error("json mValueType==ObjectVal required");
return mObjectVal.HasKeys(keys, key_count); return mObjectVal.HasKeys(keys, key_count);
} }
int Value::ToInt() const
{
if (!IsNumeric())
throw std::runtime_error("json mValueType==IsNumeric() required");
return mIntVal;
}
float Value::ToFloat() const
{
if (!IsNumeric())
throw std::runtime_error("json mValueType==IsNumeric() required");
return mFloatVal;
}
double Value::ToDouble() const
{
if (!IsNumeric())
throw std::runtime_error("json mValueType==IsNumeric() required");
return mDoubleVal;
}
bool Value::ToBool() const
{
if (mValueType != BoolVal)
throw std::runtime_error("json mValueType==BoolVal required");
return mBoolVal;
}
const std::string& Value::ToString() const
{
if (mValueType != StringVal)
throw std::runtime_error("json mValueType==StringVal required");
return mStringVal;
}
Object Value::ToObject() const
{
if (mValueType != ObjectVal)
throw std::runtime_error("json mValueType==ObjectVal required");
return mObjectVal;
}
Array Value::ToArray() const
{
if (mValueType != ArrayVal)
throw std::runtime_error("json mValueType==ArrayVal required");
return mArrayVal;
}
Value::operator int() const
{
if (!IsNumeric())
throw std::runtime_error("json mValueType==IsNumeric() required");
return mIntVal;
}
Value::operator float() const
{
if (!IsNumeric())
throw std::runtime_error("json mValueType==IsNumeric() required");
return mFloatVal;
}
Value::operator double() const
{
if (!IsNumeric())
throw std::runtime_error("json mValueType==IsNumeric() required");
return mDoubleVal;
}
Value::operator bool() const
{
if (mValueType != BoolVal)
throw std::runtime_error("json mValueType==BoolVal required");
return mBoolVal;
}
Value::operator std::string() const
{
if (mValueType != StringVal)
throw std::runtime_error("json mValueType==StringVal required");
return mStringVal;
}
Value::operator Object() const
{
if (mValueType != ObjectVal)
throw std::runtime_error("json mValueType==ObjectVal required");
return mObjectVal;
}
Value::operator Array() const
{
if (mValueType != ArrayVal)
throw std::runtime_error("json mValueType==ArrayVal required");
return mArrayVal;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Array::Array() Array::Array()
@ -420,7 +553,7 @@ std::string json::Serialize(const Value& v)
{ {
str = "{"; str = "{";
Object obj = v.ToObject(); Object obj = v.ToObject();
for (Object::ValueMap::const_iterator it = obj.begin(); it != obj.end(); it++) for (Object::ValueMap::const_iterator it = obj.begin(); it != obj.end(); ++it)
{ {
if (!first) if (!first)
str += std::string(","); str += std::string(",");
@ -435,7 +568,7 @@ std::string json::Serialize(const Value& v)
{ {
str = "["; str = "[";
Array a = v.ToArray(); Array a = v.ToArray();
for (Array::ValueVector::const_iterator it = a.begin(); it != a.end(); it++) for (Array::ValueVector::const_iterator it = a.begin(); it != a.end(); ++it)
{ {
if (!first) if (!first)
str += std::string(","); str += std::string(",");
@ -447,7 +580,8 @@ std::string json::Serialize(const Value& v)
str += "]"; str += "]";
} }
//else error // else it's not valid JSON, as a JSON data structure must be an array or an object. We'll return an empty string.
return str; return str;
} }
@ -556,7 +690,7 @@ static std::string UnescapeJSONString(const std::string& str)
{ {
std::string s = ""; std::string s = "";
for (int i = 0; i < str.length(); i++) for (std::string::size_type i = 0; i < str.length(); i++)
{ {
char c = str[i]; char c = str[i];
if ((c == '\\') && (i + 1 < str.length())) if ((c == '\\') && (i + 1 < str.length()))
@ -577,7 +711,7 @@ static std::string UnescapeJSONString(const std::string& str)
case 'f' : s.push_back('\f'); break; case 'f' : s.push_back('\f'); break;
case 'u' : skip_ahead = 5; case 'u' : skip_ahead = 5;
hex_str = str.substr(i + 4, 2); hex_str = str.substr(i + 4, 2);
hex = (unsigned int)std::strtoul(hex_str.c_str(), NULL, 16); hex = (unsigned int)std::strtoul(hex_str.c_str(), nullptr, 16);
s.push_back((char)hex); s.push_back((char)hex);
break; break;
@ -605,6 +739,7 @@ static Value DeserializeValue(std::string& str, bool* had_error, std::stack<Stac
if (str[0] == '[') if (str[0] == '[')
{ {
// This value is an array, determine the end of it and then deserialize the array
depth_stack.push(InArray); depth_stack.push(InArray);
size_t i = GetEndOfArrayOrObj(str, depth_stack); size_t i = GetEndOfArrayOrObj(str, depth_stack);
if (i == std::string::npos) if (i == std::string::npos)
@ -619,6 +754,7 @@ static Value DeserializeValue(std::string& str, bool* had_error, std::stack<Stac
} }
else if (str[0] == '{') else if (str[0] == '{')
{ {
// This value is an object, determine the end of it and then deserialize the object
depth_stack.push(InObject); depth_stack.push(InObject);
size_t i = GetEndOfArrayOrObj(str, depth_stack); size_t i = GetEndOfArrayOrObj(str, depth_stack);
@ -634,6 +770,7 @@ static Value DeserializeValue(std::string& str, bool* had_error, std::stack<Stac
} }
else if (str[0] == '\"') else if (str[0] == '\"')
{ {
// This value is a string
size_t end_quote = GetQuotePos(str, 1); size_t end_quote = GetQuotePos(str, 1);
if (end_quote == std::string::npos) if (end_quote == std::string::npos)
{ {
@ -646,19 +783,52 @@ static Value DeserializeValue(std::string& str, bool* had_error, std::stack<Stac
} }
else else
{ {
// it's not an object, string, or array so it's either a boolean or a number or null.
// Numbers can contain an exponent indicator ('e') or a decimal point.
bool has_dot = false; bool has_dot = false;
bool has_e = false; bool has_e = false;
std::string temp_val; std::string temp_val;
size_t i = 0; size_t i = 0;
bool found_digit = false;
bool found_first_valid_char = false;
for (; i < str.length(); i++) for (; i < str.length(); i++)
{ {
if (str[i] == '.') if (str[i] == '.')
{
if (!found_digit)
{
// As per JSON standards, there must be a digit preceding a decimal point
*had_error = true;
return Value();
}
has_dot = true; has_dot = true;
else if (str[i] == 'e') }
has_e = true; else if ((str[i] == 'e') || (str[i] == 'E'))
{
if ((_stricmp(temp_val.c_str(), "fals") != 0) && (_stricmp(temp_val.c_str(), "tru") != 0))
{
// it's not a boolean, check for scientific notation validity. This will also trap booleans with extra 'e' characters like falsee/truee
if (!found_digit)
{
// As per JSON standards, a digit must precede the 'e' notation
*had_error = true;
return Value();
}
else if (has_e)
{
// multiple 'e' characters not allowed
*had_error = true;
return Value();
}
has_e = true;
}
}
else if (str[i] == ']') else if (str[i] == ']')
{ {
if (depth_stack.top() != InArray) if (depth_stack.empty() || (depth_stack.top() != InArray))
{ {
*had_error = true; *had_error = true;
return Value(); return Value();
@ -668,7 +838,7 @@ static Value DeserializeValue(std::string& str, bool* had_error, std::stack<Stac
} }
else if (str[i] == '}') else if (str[i] == '}')
{ {
if (depth_stack.top() != InObject) if (depth_stack.empty() || (depth_stack.top() != InObject))
{ {
*had_error = true; *had_error = true;
return Value(); return Value();
@ -677,10 +847,22 @@ static Value DeserializeValue(std::string& str, bool* had_error, std::stack<Stac
depth_stack.pop(); depth_stack.pop();
} }
else if (str[i] == ',') else if (str[i] == ',')
break; break;
else if ((str[i] == '[') || (str[i] == '{'))
{
// error, we're supposed to be processing things besides arrays/objects in here
*had_error = true;
return Value();
}
if (!std::isspace(str[i])) if (!std::isspace(str[i]))
{
if (std::isdigit(str[i]))
found_digit = true;
found_first_valid_char = true;
temp_val += str[i]; temp_val += str[i];
}
} }
// store all floating point as doubles. This will also set the float and int values as well. // store all floating point as doubles. This will also set the float and int values as well.
@ -689,17 +871,61 @@ static Value DeserializeValue(std::string& str, bool* had_error, std::stack<Stac
else if (_stricmp(temp_val.c_str(), "false") == 0) else if (_stricmp(temp_val.c_str(), "false") == 0)
v = Value(false); v = Value(false);
else if (has_e || has_dot) else if (has_e || has_dot)
v = Value(atof(temp_val.c_str())); {
char* end_char;
errno = 0;
double d = strtod(temp_val.c_str(), &end_char);
if ((errno != 0) || (*end_char != '\0'))
{
// invalid conversion or out of range
*had_error = true;
return Value();
}
v = Value(d);
}
else if (_stricmp(temp_val.c_str(), "null") == 0) else if (_stricmp(temp_val.c_str(), "null") == 0)
v = Value(); v = Value();
else else
{ {
// Check if the value is beyond the size of an int and if so, store it as a double // Check if the value is beyond the size of an int and if so, store it as a double
double tmp_val = atof(temp_val.c_str()); char* end_char;
if ((tmp_val >= (double)INT_MIN) && (tmp_val <= (double)INT_MAX)) errno = 0;
v = Value(atoi(temp_val.c_str())); long int ival = strtol(temp_val.c_str(), &end_char, 10);
if (*end_char != '\0')
{
// invalid character sequence, not a number
*had_error = true;
return Value();
}
else if ((errno == ERANGE) && ((ival == LONG_MAX) || (ival == LONG_MIN)))
{
// value is out of range for a long int, should be a double then. See if we can convert it correctly.
errno = 0;
double dval = strtod(temp_val.c_str(), &end_char);
if ((errno != 0) || (*end_char != '\0'))
{
// error in conversion or it's too big for a double
*had_error = true;
return Value();
}
v = Value(dval);
}
else if ((ival >= INT_MIN) && (ival <= INT_MAX))
{
// valid integer range
v = Value((int)ival);
}
else else
v = Value(tmp_val); {
// probably running on a very old OS since this block implies that long isn't the same size as int.
// int is guaranteed to be at least 16 bits and long 32 bits...however nowadays they're almost
// always the same 32 bit size. But it's possible someone is running this on a very old architecture
// so for correctness, we'll error out here
*had_error = true;
return Value();
}
} }
str = str.substr(i, str.length()); str = str.substr(i, str.length());
@ -715,11 +941,13 @@ static Value DeserializeArray(std::string& str, std::stack<StackDepthType>& dept
str = Trim(str); str = Trim(str);
// Arrays begin and end with [], so if we don't find one, it's an error
if ((str[0] == '[') && (str[str.length() - 1] == ']')) if ((str[0] == '[') && (str[str.length() - 1] == ']'))
str = str.substr(1, str.length() - 2); str = str.substr(1, str.length() - 2);
else else
return Value(); return Value();
// extract out all values from the array (remember, a value can also be an array or an object)
while (str.length() > 0) while (str.length() > 0)
{ {
std::string tmp; std::string tmp;
@ -776,11 +1004,13 @@ static Value DeserializeObj(const std::string& _str, std::stack<StackDepthType>&
std::string str = Trim(_str); std::string str = Trim(_str);
// Objects begin and end with {} so if we don't find a pair, it's an error
if ((str[0] != '{') && (str[str.length() - 1] != '}')) if ((str[0] != '{') && (str[str.length() - 1] != '}'))
return Value(); return Value();
else else
str = str.substr(1, str.length() - 2); str = str.substr(1, str.length() - 2);
// Get all key/value pairs in this object...
while (str.length() > 0) while (str.length() > 0)
{ {
// Get the key name // Get the key name
@ -797,6 +1027,8 @@ static Value DeserializeObj(const std::string& _str, std::stack<StackDepthType>&
bool had_error = false; bool had_error = false;
str = str.substr(colon_idx + 1, str.length()); str = str.substr(colon_idx + 1, str.length());
// We have the key, now extract the value from the string
obj[key] = DeserializeValue(str, &had_error, depth_stack); obj[key] = DeserializeValue(str, &had_error, depth_stack);
if (had_error) if (had_error)
return Value(); return Value();

View File

@ -26,6 +26,55 @@
CHANGELOG: CHANGELOG:
========== ==========
8/31/2014:
---------
* Fixed bug from last update that broke false/true boolean usage. Courtesy of Vasi B.
* Change postfix increment of iterators in Serialize to prefix, courtesy of Vasi B.
* More improvements to validity checking of non string/object/array types. Should
catch even more invalid usage types such as -1jE5, falsee, trueeeeeee
{"key" : potato} (that should be {"key" : "potato"}), etc.
* Switched to strtol and strtod from atof/atoi in Serialize for better error handling.
* Fix for GCC order of initialization warnings, courtsey of Vasi B.
8/17/2014:
----------
* Better error handling (and bug fixing) for invalid JSON. Previously, something such as:
{"def": j[{"a": 100}],"abc": 123}
would result in at best, a crash, and at worst, nothing when this was passed to
the Deserialize method. Note that the "j" is invalid in this example. This led
to further fixes for other invalid syntax:
- Use of multiple 'e', for example: 1ee4 is not valid
- Use of '.' when not preceded by a digit is invalid. For example: .1 is
incorrect, but 0.1 is fine.
- Using 'e' when not preceded by a digit. For example, e4 isn't valid but 1e4 is.
The deserialize method should properly handle these problems and when there's an
error, it returns a Value object with the NULLVal type. Check this type to see
if there's an error.
Issue reported by Imre Pechan.
7/21/2014:
----------
* All asserts removed and replaced with exceptions, as per request from many users.
Instead of asserting, functions will throw a std::runtime_error with
appropriate error message.
* Added versions of the Value::To* functions that take a default parameter.
In the event of an error (like calling Value::ToInt() when it's type is an Object),
the default value you specified will be returned. Courtesy of PeterSvP
* Fixed type mismatch warning, courtesy of Per Rovegård
* Initialized some variables in the various Value constructors to defaults for
better support with full blast g++ warnings, courtesy of Mark Odell.
* Changed Value::ToString to return a const std::string& instead of std::string
to avoid unnecessary copying.
* Improved some commenting
* Fixed a bug where a capital E for scientific notation numbers wasn't
recognized, only lowercase e.
* VASTLY OVERHAULED AND IMPROVED THE README FILE, PLEASE CONSULT IT FOR
IN DEPTH USAGE AND EXAMPLES.
2/8/2014: 2/8/2014:
--------- ---------
MAJOR BUG FIXES, all courtesy of Per Rovegård, Ph.D. MAJOR BUG FIXES, all courtesy of Per Rovegård, Ph.D.
@ -70,7 +119,7 @@
1/27/2014 1/27/2014
---------- ----------
* Deserialize will now return a NULLType Value instance if there was an * Deserialize will now return a NULLVal Value instance if there was an
error instead of asserting. This way you can handle however you want to error instead of asserting. This way you can handle however you want to
invalid JSON being passed in. As a top level object must be either an invalid JSON being passed in. As a top level object must be either an
array or an object, a NULL value return indicates an invalid result. array or an object, a NULL value return indicates an invalid result.
@ -187,8 +236,10 @@
#include <vector> #include <vector>
#include <map> #include <map>
#include <string> #include <string>
#include <assert.h> #include <stdexcept>
// PLEASE SEE THE README FOR USAGE INFORMATION AND EXAMPLES. Comments will be kept to a minimum to reduce clutter.
namespace json namespace json
{ {
enum ValueType enum ValueType
@ -205,10 +256,15 @@ namespace json
class Value; class Value;
// Represents a JSON object which is of the form {string:value, string:value, ...} Where string is the "key" name and is
// of the form "" or "characters". Value is either of: string, number, object, array, boolean, null
class Object class Object
{ {
public: public:
// This is the type used to store key/value pairs. If you want to get an iterator for this class to iterate over its members,
// use this.
// For example: Object::ValueMap::iterator my_iterator;
typedef std::map<std::string, Value> ValueMap; typedef std::map<std::string, Value> ValueMap;
protected: protected:
@ -229,6 +285,11 @@ namespace json
inline friend bool operator <=(const Object& lhs, const Object& rhs) {return !operator>(lhs, rhs);} inline friend bool operator <=(const Object& lhs, const Object& rhs) {return !operator>(lhs, rhs);}
inline friend bool operator >=(const Object& lhs, const Object& rhs) {return !operator<(lhs, rhs);} inline friend bool operator >=(const Object& lhs, const Object& rhs) {return !operator<(lhs, rhs);}
// Just like a std::map, you can get the value for a key by using the index operator. You could also
// use this to insert a value if it doesn't exist, or overwrite it if it does. Example:
// Value my_val = my_object["some key name"];
// my_object["some key name"] = "overwriting the value with this new string value";
// my_object["new key name"] = "a new key being inserted";
Value& operator [](const std::string& key); Value& operator [](const std::string& key);
const Value& operator [](const std::string& key) const; const Value& operator [](const std::string& key) const;
Value& operator [](const char* key); Value& operator [](const char* key);
@ -239,11 +300,12 @@ namespace json
ValueMap::iterator begin(); ValueMap::iterator begin();
ValueMap::iterator end(); ValueMap::iterator end();
// Find will return end() if the key can't be found, just like std::map does. // Find will return end() if the key can't be found, just like std::map does. ->first will be the key (a std::string),
// ->second will be the Value.
ValueMap::iterator find(const std::string& key); ValueMap::iterator find(const std::string& key);
ValueMap::const_iterator find(const std::string& key) const; ValueMap::const_iterator find(const std::string& key) const;
// Convenience wrapper to find to search for a key // Convenience wrapper to search for a key
bool HasKey(const std::string& key) const; bool HasKey(const std::string& key) const;
// Checks if the object contains all the keys in the array. If it does, returns -1. // Checks if the object contains all the keys in the array. If it does, returns -1.
@ -258,10 +320,14 @@ namespace json
}; };
// Represents a JSON Array which is of the form [value, value, ...] where value is either of: string, number, object, array, boolean, null
class Array class Array
{ {
public: public:
// This is the type used to store values. If you want to get an iterator for this class to iterate over its members,
// use this.
// For example: Array::ValueVector::iterator my_array_iterator;
typedef std::vector<Value> ValueVector; typedef std::vector<Value> ValueVector;
protected: protected:
@ -305,6 +371,7 @@ namespace json
size_t size() const; size_t size() const;
}; };
// Represents a JSON value which is either of: string, number, object, array, boolean, null
class Value class Value
{ {
protected: protected:
@ -321,18 +388,23 @@ namespace json
public: public:
Value() : mValueType(NULLVal), mIntVal(0), mFloatVal(0), mDoubleVal(0), mBoolVal(false) {} Value() : mValueType(NULLVal), mIntVal(0), mFloatVal(0), mDoubleVal(0), mBoolVal(false) {}
Value(int v) : mValueType(IntVal), mIntVal(v), mFloatVal((float)v), mDoubleVal((double)v) {} Value(int v) : mValueType(IntVal), mIntVal(v), mFloatVal((float)v), mDoubleVal((double)v), mBoolVal(false) {}
Value(float v) : mValueType(FloatVal), mFloatVal(v), mIntVal((int)v), mDoubleVal((double)v) {} Value(float v) : mValueType(FloatVal), mIntVal((int)v), mFloatVal(v), mDoubleVal((double)v), mBoolVal(false) {}
Value(double v) : mValueType(DoubleVal), mDoubleVal(v), mIntVal((int)v), mFloatVal((float)v) {} Value(double v) : mValueType(DoubleVal), mIntVal((int)v), mFloatVal((float)v), mDoubleVal(v), mBoolVal(false) {}
Value(const std::string& v) : mValueType(StringVal), mStringVal(v) {} Value(const std::string& v) : mValueType(StringVal), mIntVal(), mFloatVal(), mDoubleVal(), mStringVal(v), mBoolVal(false) {}
Value(const char* v) : mValueType(StringVal), mStringVal(v) {} Value(const char* v) : mValueType(StringVal), mIntVal(), mFloatVal(), mDoubleVal(), mStringVal(v), mBoolVal(false) {}
Value(const Object& v) : mValueType(ObjectVal), mObjectVal(v) {} Value(const Object& v) : mValueType(ObjectVal), mIntVal(), mFloatVal(), mDoubleVal(), mObjectVal(v), mBoolVal(false) {}
Value(const Array& v) : mValueType(ArrayVal), mArrayVal(v) {} Value(const Array& v) : mValueType(ArrayVal), mIntVal(), mFloatVal(), mDoubleVal(), mArrayVal(v), mBoolVal(false) {}
Value(const bool v) : mValueType(BoolVal), mBoolVal(v) {} Value(bool v) : mValueType(BoolVal), mIntVal(), mFloatVal(), mDoubleVal(), mBoolVal(v) {}
Value(const Value& v); Value(const Value& v);
// Use this to determine the underlying type that this Value class represents. It will be one of the
// ValueType enums as defined at the top of this file.
ValueType GetType() const {return mValueType;} ValueType GetType() const {return mValueType;}
// Convenience method that checks if this type is an int/double/float
bool IsNumeric() const {return (mValueType == IntVal) || (mValueType == DoubleVal) || (mValueType == FloatVal);}
Value& operator =(const Value& v); Value& operator =(const Value& v);
friend bool operator ==(const Value& lhs, const Value& rhs); friend bool operator ==(const Value& lhs, const Value& rhs);
@ -343,7 +415,9 @@ namespace json
inline friend bool operator >=(const Value& lhs, const Value& rhs) {return !operator<(lhs, rhs);} inline friend bool operator >=(const Value& lhs, const Value& rhs) {return !operator<(lhs, rhs);}
// For use with Array/ObjectVal types, respectively // If this value represents an object or array, you can use the [] indexing operator
// just like you would with the native json::Array or json::Object classes.
// THROWS A std::runtime_error IF NOT AN ARRAY OR OBJECT.
Value& operator [](size_t idx); Value& operator [](size_t idx);
const Value& operator [](size_t idx) const; const Value& operator [](size_t idx) const;
Value& operator [](const std::string& key); Value& operator [](const std::string& key);
@ -351,19 +425,30 @@ namespace json
Value& operator [](const char* key); Value& operator [](const char* key);
const Value& operator [](const char* key) const; const Value& operator [](const char* key) const;
// If this value represents an object, these methods let you check if a single key or an array of
// keys is contained within it.
// THROWS A std::runtime_error IF NOT AN OBJECT.
bool HasKey(const std::string& key) const; bool HasKey(const std::string& key) const;
int HasKeys(const std::vector<std::string>& keys) const; int HasKeys(const std::vector<std::string>& keys) const;
int HasKeys(const char* keys[], int key_count) const; int HasKeys(const char* keys[], int key_count) const;
// non-operator versions // non-operator versions, **will throw a std::runtime_error if invalid with an appropriate error message**
int ToInt() const {assert(IsNumeric()); return mIntVal;} int ToInt() const;
float ToFloat() const {assert(IsNumeric()); return mFloatVal;} float ToFloat() const;
double ToDouble() const {assert(IsNumeric()); return mDoubleVal;} double ToDouble() const;
bool ToBool() const {assert(mValueType == BoolVal); return mBoolVal;} bool ToBool() const;
std::string ToString() const {assert(mValueType == StringVal); return mStringVal;} const std::string& ToString() const;
Object ToObject() const {assert(mValueType == ObjectVal); return mObjectVal;} Object ToObject() const;
Array ToArray() const {assert(mValueType == ArrayVal); return mArrayVal;} Array ToArray() const;
// These versions do the same as above but will return your specified default value in the event there's an error, and thus **don't** throw an exception.
int ToInt(int def) const {return IsNumeric() ? mIntVal : def;}
float ToFloat(float def) const {return IsNumeric() ? mFloatVal : def;}
double ToDouble(double def) const {return IsNumeric() ? mDoubleVal : def;}
bool ToBool(bool def) const {return (mValueType == BoolVal) ? mBoolVal : def;}
const std::string& ToString(const std::string& def) const {return (mValueType == StringVal) ? mStringVal : def;}
// Please note that as per C++ rules, implicitly casting a Value to a std::string won't work. // Please note that as per C++ rules, implicitly casting a Value to a std::string won't work.
// This is because it could use the int/float/double/bool operators as well. So to assign a // This is because it could use the int/float/double/bool operators as well. So to assign a
@ -372,15 +457,13 @@ namespace json
// Or you can now do: // Or you can now do:
// my_string = my_value.ToString(); // my_string = my_value.ToString();
// //
operator int() const {assert(IsNumeric()); return mIntVal;} operator int() const;
operator float() const {assert(IsNumeric()); return mFloatVal;} operator float() const;
operator double() const {assert(IsNumeric()); return mDoubleVal;} operator double() const;
operator bool() const {assert(mValueType == BoolVal); return mBoolVal;} operator bool() const;
operator std::string() const {assert(mValueType == StringVal); return mStringVal;} operator std::string() const;
operator Object() const {assert(mValueType == ObjectVal); return mObjectVal;} operator Object() const;
operator Array() const {assert(mValueType == ArrayVal); return mArrayVal;} operator Array() const;
bool IsNumeric() const {return (mValueType == IntVal) || (mValueType == DoubleVal) || (mValueType == FloatVal);}
// Returns 1 for anything not an Array/ObjectVal // Returns 1 for anything not an Array/ObjectVal
size_t size() const; size_t size() const;
@ -392,10 +475,16 @@ namespace json
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Converts a JSON Object or Array instance into a JSON string representing it. // Converts a JSON Object or Array instance into a JSON string representing it. RETURNS EMPTY STRING ON ERROR.
// As per JSON specification, a JSON data structure must be an array or an object. Thus, you must either pass in a
// json::Array, json::Object, or a json::Value that has an Array or Object as its underlying type.
std::string Serialize(const Value& obj); std::string Serialize(const Value& obj);
// If there is an error, Value will be NULLType // If there is an error, Value will be NULLVal. Pass in a valid JSON string (such as one returned from Serialize, or obtained
// elsewhere) to receive a Value in return that represents the JSON structure. Check the type of Value by calling GetType().
// It will be ObjectVal or ArrayVal (or NULLVal if invalid JSON). The Value class contains the operator [] for indexing in the
// case that the underlying type is an object or array. You may, if you prefer, create an object or array from the Value returned
// by this method by simply passing it into the constructor.
Value Deserialize(const std::string& str); Value Deserialize(const std::string& str);
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////