This commit is contained in:
2021-10-14 13:47:35 +02:00
commit 6625a8dfaa
4026 changed files with 844291 additions and 0 deletions
@@ -0,0 +1,665 @@
#include "json.h"
#include <cstdlib>
#include <string>
#include <algorithm>
#include <cstdlib>
#include <cstdio>
#include <climits>
#include <cstring>
#include <functional>
#include <cctype>
#include <stack>
#ifndef WIN32
# define _stricmp strcasecmp
#endif
#ifdef _MSC_VER
# define snprintf sprintf_s
#endif
using namespace json;
namespace json {
enum StackDepthType
{
InObject,
InArray
};
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Helper functions
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
static std::string Trim(const std::string& str)
{
std::string s = str;
// remove white space in front
s.erase(s.begin(), std::find_if(s.begin(), s.end(), std::not1(std::ptr_fun<int, int>(std::isspace))));
// remove trailing white space
s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun<int, int>(std::isspace))).base(), s.end());
return s;
}
// Finds the position of the first " character that is NOT preceeded immediately by a \ character.
// In JSON, \" is valid and has a different meaning than the escaped " character.
static size_t GetQuotePos(const std::string& str, const size_t start_pos = 0)
{
bool found_slash = false;
for (size_t i = start_pos; i < str.length(); ++i)
{
const char c = str[i];
if ((c == '\\') && !found_slash)
{
found_slash = true;
continue;
}
if ((c == '\"') && !found_slash) { return i; }
found_slash = false;
}
return std::string::npos;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Value::Value(const Value& v) : mValueType(v.mValueType)
{
switch (mValueType)
{
case StringVal: mStringVal = v.mStringVal;
break;
case IntVal: mIntVal = v.mIntVal;
mFloatVal = float(v.mIntVal);
mDoubleVal = double(v.mIntVal);
break;
case FloatVal: mFloatVal = v.mFloatVal;
mIntVal = int(v.mFloatVal);
mDoubleVal = double(v.mDoubleVal);
break;
case DoubleVal: mDoubleVal = v.mDoubleVal;
mIntVal = int(v.mDoubleVal);
mFloatVal = float(v.mDoubleVal);
break;
case BoolVal: mBoolVal = v.mBoolVal;
break;
case ObjectVal: mObjectVal = v.mObjectVal;
break;
case ArrayVal: mArrayVal = v.mArrayVal;
break;
default: break;
}
}
Value& Value::operator =(const Value& v)
{
if (&v == this) { return *this; }
mValueType = v.mValueType;
switch (mValueType)
{
case StringVal: mStringVal = v.mStringVal;
break;
case IntVal: mIntVal = v.mIntVal;
mFloatVal = float(v.mIntVal);
mDoubleVal = double(v.mIntVal);
break;
case FloatVal: mFloatVal = v.mFloatVal;
mIntVal = int(v.mFloatVal);
mDoubleVal = double(v.mDoubleVal);
break;
case DoubleVal: mDoubleVal = v.mDoubleVal;
mIntVal = int(v.mDoubleVal);
mFloatVal = float(v.mDoubleVal);
break;
case BoolVal: mBoolVal = v.mBoolVal;
break;
case ObjectVal: mObjectVal = v.mObjectVal;
break;
case ArrayVal: mArrayVal = v.mArrayVal;
break;
default: break;
}
return *this;
}
Value& Value::operator [](const size_t idx)
{
assert(mValueType == ArrayVal);
return mArrayVal[idx];
}
const Value& Value::operator [](const size_t idx) const
{
assert(mValueType == ArrayVal);
return mArrayVal[idx];
}
Value& Value::operator [](const std::string& key)
{
assert(mValueType == ObjectVal);
return mObjectVal[key];
}
Value& Value::operator [](const char* key)
{
assert(mValueType == ObjectVal);
return mObjectVal[key];
}
const Value& Value::operator [](const char* key) const
{
assert(mValueType == ObjectVal);
return mObjectVal[key];
}
const Value& Value::operator [](const std::string& key) const
{
assert(mValueType == ObjectVal);
return mObjectVal[key];
}
size_t Value::size() const
{
if ((mValueType != ObjectVal) && (mValueType != ArrayVal)) { return 1; }
return mValueType == ObjectVal ? mObjectVal.size() : mArrayVal.size();
}
bool Value::HasKey(const std::string& key) const
{
assert(mValueType == ObjectVal);
return mObjectVal.HasKey(key);
}
int Value::HasKeys(const std::vector<std::string>& keys) const
{
assert(mValueType == ObjectVal);
return mObjectVal.HasKeys(keys);
}
int Value::HasKeys(const char** keys, const int key_count) const
{
assert(mValueType == ObjectVal);
return mObjectVal.HasKeys(keys, key_count);
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Array& Array::operator =(const Array& a)
{
if (&a == this) { return *this; }
Clear();
mValues = a.mValues;
return *this;
}
Value& Array::operator [](const size_t i) { return mValues[i]; }
const Value& Array::operator [](const size_t i) const { return mValues[i]; }
Array::ValueVector::const_iterator Array::begin() const { return mValues.begin(); }
Array::ValueVector::const_iterator Array::end() const { return mValues.end(); }
Array::ValueVector::iterator Array::begin() { return mValues.begin(); }
Array::ValueVector::iterator Array::end() { return mValues.end(); }
void Array::push_back(const Value& v) { mValues.push_back(v); }
void Array::insert(const size_t index, const Value& v) { mValues.insert(mValues.begin() + index, v); }
Array::ValueVector::iterator Array::find(const Value& v) { return std::find(mValues.begin(), mValues.end(), v); }
Array::ValueVector::const_iterator Array::find(const Value& v) const { return std::find(mValues.begin(), mValues.end(), v); }
bool Array::HasValue(const Value& v) const { return find(v) != end(); }
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Object& Object::operator =(const Object& obj)
{
if (&obj == this) { return *this; }
Clear();
mValues = obj.mValues;
return *this;
}
Value& Object::operator [](const std::string& key) { return mValues[key]; }
const Value& Object::operator [](const std::string& key) const { return mValues.find(key)->second; }
Value& Object::operator [](const char* key) { return mValues[key]; }
const Value& Object::operator [](const char* key) const { return mValues.find(key)->second; }
Object::ValueMap::const_iterator Object::begin() const { return mValues.begin(); }
Object::ValueMap::const_iterator Object::end() const { return mValues.end(); }
Object::ValueMap::iterator Object::begin() { return mValues.begin(); }
Object::ValueMap::iterator Object::end() { return mValues.end(); }
Object::ValueMap::iterator Object::find(const std::string& key) { return mValues.find(key); }
Object::ValueMap::const_iterator Object::find(const std::string& key) const { return mValues.find(key); }
bool Object::HasKey(const std::string& key) const { return find(key) != end(); }
int Object::HasKeys(const std::vector<std::string>& keys) const
{
for (size_t i = 0; i < keys.size(); ++i) { if (!HasKey(keys[i])) { return int(i); } }
return -1;
}
int Object::HasKeys(const char** keys, const int key_count) const
{
for (int i = 0; i < key_count; ++i) { if (!HasKey(keys[i])) { return i; } }
return -1;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
std::string SerializeArray(const Array& a);
std::string SerializeValue(const Value& v)
{
std::string str;
static const int BUFF_SZ = 500;
char buff[BUFF_SZ];
switch (v.GetType())
{
case IntVal: snprintf(buff, BUFF_SZ, "%d", int(v));
str = buff;
break;
case FloatVal: snprintf(buff, BUFF_SZ, "%f", float(v));
str = buff;
break;
case DoubleVal: snprintf(buff, BUFF_SZ, "%f", double(v));
str = buff;
break;
case BoolVal: str = v ? "true" : "false";
break;
case nullptrVal: str = "null";
break;
case ObjectVal: str = Serialize(v);
break;
case ArrayVal: str = SerializeArray(v);
break;
case StringVal: str = std::string("\"") + v.ToString() + std::string("\"");
break;
}
return str;
}
std::string SerializeArray(const Array& a)
{
std::string str = "[";
bool first = true;
for (const auto& v : a)
{
if (!first) { str += std::string(","); }
str += SerializeValue(v);
first = false;
}
str += "]";
return str;
}
std::string json::Serialize(const Value& v)
{
std::string str;
bool first = true;
if (v.GetType() == ObjectVal)
{
str = "{";
Object obj = v.ToObject();
for (auto it = obj.begin(); it != obj.end(); ++it)
{
if (!first) { str += std::string(","); }
str += std::string("\"") + it->first + std::string("\":") + SerializeValue(it->second);
first = false;
}
str += "}";
}
else if (v.GetType() == ArrayVal)
{
str = "[";
Array a = v.ToArray();
for (auto it = a.begin(); it != a.end(); ++it)
{
if (!first) { str += std::string(","); }
str += SerializeValue(*it);
first = false;
}
str += "]";
}
//else error
return str;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
static Value DeserializeArray(std::string& str, std::stack<StackDepthType>& depth_stack);
static Value DeserializeObj(const std::string& _str, std::stack<StackDepthType>& depth_stack);
static Value DeserializeInternal(const std::string& _str, std::stack<StackDepthType>& depth_stack)
{
Value v;
if (_str.length() == 0) { return v; }
std::string str = Trim(_str);
if (str[0] == '{')
{
// Error: Began with a { but doesn't end with one
if (str[str.length() - 1] != '}') { return Value(); }
depth_stack.push(InObject);
v = DeserializeObj(str, depth_stack);
if ((v.GetType() == nullptrVal) || (depth_stack.top() != InObject)) { return v; }
depth_stack.pop();
}
else if (str[0] == '[')
{
// Error: Began with a [ but doesn't end with one
if (str[str.length() - 1] != ']') { return Value(); }
depth_stack.push(InArray);
v = DeserializeArray(str, depth_stack);
if ((v.GetType() == nullptrVal) || (depth_stack.top() != InArray)) { return v; }
depth_stack.pop();
}
else { return Value(); } // Will never get here unless _str is not valid JSON
return v;
}
static size_t GetEndOfArrayOrObj(const std::string& str, std::stack<StackDepthType>& depth_stack)
{
size_t i = 1;
bool in_quote = false;
const size_t original_count = depth_stack.size();
for (; i < str.length(); ++i)
{
if (str[i] == '\"') { if (str[i - 1] != '\\') { in_quote = !in_quote; } }
else if (!in_quote)
{
if (str[i] == '[') { depth_stack.push(InArray); }
else if (str[i] == '{') { depth_stack.push(InObject); }
else if (str[i] == ']')
{
const StackDepthType t = depth_stack.top();
// expected to be closing an array but instead we're inside an object block.
// Example problem: {]}
if (t != InArray) { return std::string::npos; }
const size_t count = depth_stack.size();
depth_stack.pop();
if (count == original_count) { break; }
}
else if (str[i] == '}')
{
const StackDepthType t = depth_stack.top();
// expected to be closing an object but instead we're inside an array.
// Example problem: [}]
if (t != InObject) { return std::string::npos; }
const size_t count = depth_stack.size();
depth_stack.pop();
if (count == original_count) { break; }
}
}
}
return i;
}
static std::string UnescapeJSONString(const std::string& str)
{
std::string s;
for (size_t i = 0; i < str.length(); ++i)
{
const char c = str[i];
if ((c == '\\') && (i + 1 < str.length()))
{
int skip_ahead = 1;
unsigned int hex;
std::string hex_str;
switch (str[i + 1])
{
case '"': s.push_back('\"');
break;
case '\\': s.push_back('\\');
break;
case '/': s.push_back('/');
break;
case 't': s.push_back('\t');
break;
case 'n': s.push_back('\n');
break;
case 'r': s.push_back('\r');
break;
case 'b': s.push_back('\b');
break;
case 'f': s.push_back('\f');
break;
case 'u': skip_ahead = 5;
hex_str = str.substr(i + 4, 2);
hex = static_cast<unsigned int>(std::strtoul(hex_str.c_str(), nullptr, 16));
s.push_back(char(hex));
break;
default: break;
}
i += skip_ahead;
}
else { s.push_back(c); }
}
return Trim(s);
}
static Value DeserializeValue(std::string& str, bool* had_error, std::stack<StackDepthType>& depth_stack)
{
Value v;
*had_error = false;
str = Trim(str);
if (str.length() == 0) { return v; }
if (str[0] == '[')
{
depth_stack.push(InArray);
size_t i = GetEndOfArrayOrObj(str, depth_stack);
if (i == std::string::npos)
{
*had_error = true;
return Value();
}
std::string array_str = str.substr(0, i + 1);
v = Value(DeserializeArray(array_str, depth_stack));
str = str.substr(i + 1, str.length());
}
else if (str[0] == '{')
{
depth_stack.push(InObject);
size_t i = GetEndOfArrayOrObj(str, depth_stack);
if (i == std::string::npos)
{
*had_error = true;
return Value();
}
std::string obj_str = str.substr(0, i + 1);
v = Value(DeserializeInternal(obj_str, depth_stack));
str = str.substr(i + 1, str.length());
}
else if (str[0] == '\"')
{
size_t end_quote = GetQuotePos(str, 1);
if (end_quote == std::string::npos)
{
*had_error = true;
return Value();
}
v = Value(UnescapeJSONString(str.substr(1, end_quote - 1)));
str = str.substr(end_quote + 1, str.length());
}
else
{
bool has_dot = false;
bool has_e = false;
std::string temp_val;
size_t i = 0;
for (; i < str.length(); ++i)
{
if (str[i] == '.') { has_dot = true; }
else if (str[i] == 'e') { has_e = true; }
else if (str[i] == ']')
{
if (depth_stack.top() != InArray)
{
*had_error = true;
return Value();
}
depth_stack.pop();
}
else if (str[i] == '}')
{
if (depth_stack.top() != InObject)
{
*had_error = true;
return Value();
}
depth_stack.pop();
}
else if (str[i] == ',') { break; }
if (std::isspace(str[i]) == 0) { temp_val += str[i]; }
}
// store all floating point as doubles. This will also set the float and int values as well.
if (_stricmp(temp_val.c_str(), "true") == 0) { v = Value(true); }
else if (_stricmp(temp_val.c_str(), "false") == 0) { v = Value(false); }
else if (has_e || has_dot) { v = Value(strtod(temp_val.c_str(), nullptr)); }
else if (_stricmp(temp_val.c_str(), "null") == 0) { v = Value(); }
else
{
// Check if the value is beyond the size of an int and if so, store it as a double
double tmp_val = strtod(temp_val.c_str(), nullptr);
if ((tmp_val >= double(INT_MIN)) && (tmp_val <= double(INT_MAX))) { v = Value(int(strtol(temp_val.c_str(), nullptr, 10))); }
else { v = Value(tmp_val); }
}
str = str.substr(i, str.length());
}
return v;
}
static Value DeserializeArray(std::string& str, std::stack<StackDepthType>& depth_stack)
{
Array a;
bool had_error = false;
str = Trim(str);
if ((str[0] == '[') && (str[str.length() - 1] == ']')) { str = str.substr(1, str.length() - 2); }
else { return Value(); }
while (str.length() > 0)
{
std::string tmp;
size_t i = 0;
for (; i < str.length(); ++i)
{
// If we get to an object or array, parse it:
if ((str[i] == '{') || (str[i] == '['))
{
Value v = DeserializeValue(str, &had_error, depth_stack);
if (had_error) { return Value(); }
if (v.GetType() != nullptrVal) { a.push_back(v); }
break;
}
bool terminate_parsing = false;
if ((str[i] == ',') || (str[i] == ']'))
{
terminate_parsing = true; // hit the end of a value, parse it in the next block
}
else
{
// keep grabbing chars to build up the value
tmp += str[i];
if (i == str.length() - 1) { terminate_parsing = true; } // end of string, finish parsing
}
if (terminate_parsing)
{
Value v = DeserializeValue(tmp, &had_error, depth_stack);
if (had_error) { return Value(); }
if (v.GetType() != nullptrVal) { a.push_back(v); }
str = str.substr(i + 1, str.length());
break;
}
}
}
return a;
}
static Value DeserializeObj(const std::string& _str, std::stack<StackDepthType>& depth_stack)
{
Object obj;
std::string str = Trim(_str);
if ((str[0] != '{') && (str[str.length() - 1] != '}')) { return Value(); }
str = str.substr(1, str.length() - 2);
while (str.length() > 0)
{
// Get the key name
const size_t start_quote_idx = GetQuotePos(str);
const size_t end_quote_idx = GetQuotePos(str, start_quote_idx + 1);
const size_t colon_idx = str.find(':', end_quote_idx);
if ((start_quote_idx == std::string::npos) || (end_quote_idx == std::string::npos) || (colon_idx == std::string::npos))
{
return Value(); // can't find key name
}
std::string key = str.substr(start_quote_idx + 1, end_quote_idx - start_quote_idx - 1);
if (key.length() == 0) { return Value(); }
bool had_error = false;
str = str.substr(colon_idx + 1, str.length());
obj[key] = DeserializeValue(str, &had_error, depth_stack);
if (had_error) { return Value(); }
}
return obj;
}
Value json::Deserialize(const std::string& str)
{
std::stack<StackDepthType> depth_stack;
return DeserializeInternal(str, depth_stack);
}
@@ -0,0 +1,562 @@
/*
SuperEasyJSON
http://www.sourceforge.net/p/supereasyjson
The MIT License (MIT)
Copyright (c) 2013 Jeff Weinstein (jeff.weinstein at gmail)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
CHANGELOG:
==========
2/8/2014:
---------
MAJOR BUG FIXES, all courtesy of Per Rovegård, Ph.D.
* Feature request: HasKey and HasKeys added to Value for convenience and
to avoid having to make a temporary object.
* Strings should now be properly unescaped. Previously, as an example, the
string "\/Date(1390431949211+0100)\/\" would be parsed as
\/Date(1390431949211+0100)\/. The string is now properly parsed as
/Date(1390431949211+0100)/.
As per http://www.json.org the other escape characters including
\u+4 hex digits will now be properly unescaped. So for example,
\u0061 now becomes "A".
* Serialize now supports serializing a toplevel array (which is valid JSON).
The parameter it takes is now a Value, but existing code doesn't
need to be changed.
* Fixed bug with checking for proper opening/closing sequence for braces/brackets.
Previously, this code:
const char *json = "{\"arr\":[{ }}]}";
auto val = json::Deserialize(json);
worked fine with no errors. That's a bug. I did a major overhaul so that
now improperly formatted pairs will now correctly result in an error.
* Made internal deserialize methods static
1/30/2014:
----------
* Changed #pragma once to the standard #ifndef header guard style for
better compatibility.
* Added a [] operator for Value that takes a const char* as an argument
to avoid having to explicitly (and annoyingly) cast to std::string.
Thus, my_value["asdf"] = "a string" should now work fine.
The same has been added to the Object class.
* Added non-operator methods of casting a Value to int/string/bool/etc.
Implicitly casting a Value to a std::string doesn't work as per C++
rules. As such, previously to assign a Value to a std::string you
had to do:
my_std_string = (std::string)my_value;
You can now instead do:
my_std_string = my_value.ToString();
If you want more information on why this can't be done, please read
this topic for more details:
http://stackoverflow.com/questions/3518145/c-overloading-conversion-operator-for-custom-type-to-stdstring
1/27/2014
----------
* Deserialize will now return a nullptrType Value instance if there was an
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
array or an object, a nullptr value return indicates an invalid result.
1/11/2014
---------
* Major bug fix: Strings containing []{ } characters could cause
parsing errors under certain conditions. I've just tested
the class parsing a 300KB JSON file with all manner of bizarre
characters and permutations and it worked, so hopefully this should
be the end of "major bug" fixes.
1/10/2014
---------
Bug fixes courtesy of Gerry Beauregard:
* Pretty big bug: was using wrong string paramter in ::Deserialize
and furthermore it wasn't being trimmed.
* Object::HasKeys now casts the return value to avoid compiler warnings.
* Slight optimization to the Trim function
* Made asserts in ::Deserialize easier to read
1/9/2014
--------
* Major bug fix: for JSON strings containing \" (as in, two characters,
not the escaped " character), the lib would mess up and not parse
correctly.
* Major bug fix: I erroneously was assuming that all root JSON types
had to be an object. This was an oversight, as a root JSON
object can be an array. I have therefore changed the Deserialize
method to return a json::Value rather than a json::Object. This
will NOT impact any existing code you have, as a json::Value will
cast to a json::Object (if it is indeed an object). But for
correctness, you should be using json::Value = Deserialize...
The Value type can be checked if it's an array (or any other type),
and furthermore can even be accessed with the [] operator for
convenience.
* I've made the nullptr value type set numeric fields to 0 and bool to false.
This is for convenience for using the nullptr type as a default return
value in your code.
* asserts added to casting (Gerry Beauregard)
* Added method HasKeys to json::Object which will check if all the keys
specified are in the object, returning the index of the first key
not found or -1 if all found (hoppe).
1/4/2014
--------
* Fixed bug where bools were being parsed as doubles (Gerry Beauregard).
1/2/2014 v3
------------
* More missing headers added for VisualStudio 2012
* Switched to snprintf instead of sprintf (or sprintf_s in MSVC)
1/2/2014 v2
-----------
* Added yet more missing headers for compiling on GNU and Linux systems
* Made Deserialize copy the passed in string so it won't mangle it
1/2/2014
--------
* Fixed previous changelog years. Got ahead of myself and marked them
as 2014 when they were in fact done in 2013.
* Added const version of [] to Array/Object/Value
* Removed C++11 requirements, should work with older compilers
(thanks to Meng Wang for pointing that out)
* Made ValueMap and ValueVector typedefs in Object/Value public
so you can actually iterate over the class
* Added HasKey and HasValue to Object/Array for convenience
(note this could have been done comparing .find to .end)
12/29/2013 v2
-------------
* Added .size() field to Value. Returns 1 for non Array/Object types,
otherwise the number of elements contained.
* Added .find() to Object to search for a key. Returns Object::end()
if not found, otherwise the Value.
Example: bool found = my_obj.find("some key") != my_obj.end();
* Added .find() to Array to search for a value. Just a convenience
wrapper for std::find(Array::begin(), Array::end(), Value)
* Added ==, !=, <, >, <=, >= operators to Object/Array/Value.
For Objects/Arrays, the operators function just like they do for a
std::map and std::vector, respectively.
* Added IsNumeric to Value to indicate if it's an int/float/double type.
12/29/2013
----------
* Added the DoubleVal type which stores, you guessed it, double values.
* Bug fix for floats with an exact integer value. Now, setting any numerical
field will also set the fields for the other numerical types. So if you
have obj["value"] = 12, then the int/float/double cast methods will
return 12/12.0f/12.0. Previously, in the example above, only the int
value was set, making a cast to float return 0.
* Bug fix for deserializing JSON strings that contained large integer values.
Now if the numerical value of a key in a JSON string contains a number
less than INT_MIN or greater than INT_MAX it will be stored as a double.
Note that as mentioned above, all numerical fields are set.
* Should work fine with scientific notation values now.
12/28/2013
----------
* Fixed a bug where if there were spaces around values or key names in a JSON
string passed in to Deserialize, invalid results or asserts would occur.
(Fix courtesy of Gerry Beauregard)
* Added method named "Clear()" to Object/Array/Value to reset state
* Added license to header file for easyness (totally valid word).
*/
#pragma once
#include <vector>
#include <map>
#include <string>
#include <cassert>
namespace json {
enum ValueType { nullptrVal, StringVal, IntVal, FloatVal, DoubleVal, ObjectVal, ArrayVal, BoolVal };
class Value;
class Object
{
public:
typedef std::map<std::string, Value> ValueMap;
protected:
ValueMap mValues;
public:
Object() = default;
Object(const Object& obj) : mValues(obj.mValues) {}
Object& operator =(const Object& obj);
friend bool operator ==(const Object& lhs, const Object& rhs);
friend bool operator !=(const Object& lhs, const Object& rhs) { return !(lhs == rhs); }
friend bool operator <(const Object& lhs, const Object& rhs);
friend bool operator >(const Object& lhs, const Object& rhs) { return operator<(rhs, lhs); }
friend bool operator <=(const Object& lhs, const Object& rhs) { return !operator>(lhs, rhs); }
friend bool operator >=(const Object& lhs, const Object& rhs) { return !operator<(lhs, rhs); }
Value& operator [](const std::string& key);
const Value& operator [](const std::string& key) const;
Value& operator [](const char* key);
const Value& operator [](const char* key) const;
ValueMap::const_iterator begin() const;
ValueMap::const_iterator end() const;
ValueMap::iterator begin();
ValueMap::iterator end();
// Find will return end() if the key can't be found, just like std::map does.
ValueMap::iterator find(const std::string& key);
ValueMap::const_iterator find(const std::string& key) const;
// Convenience wrapper to find to search for a key
bool HasKey(const std::string& key) const;
// Checks if the object contains all the keys in the array. If it does, returns -1.
// If it doesn't, returns the index of the first key it couldn't find.
int HasKeys(const std::vector<std::string>& keys) const;
int HasKeys(const char** keys, int key_count) const;
// Removes all values and resets the state back to default
void Clear() { mValues.clear(); }
size_t size() const { return mValues.size(); }
};
class Array
{
public:
typedef std::vector<Value> ValueVector;
protected:
ValueVector mValues;
public:
Array() = default;
Array(const Array& a) : mValues(a.mValues) { }
Array& operator =(const Array& a);
friend bool operator ==(const Array& lhs, const Array& rhs);
friend bool operator !=(const Array& lhs, const Array& rhs) { return !(lhs == rhs); }
friend bool operator <(const Array& lhs, const Array& rhs);
friend bool operator >(const Array& lhs, const Array& rhs) { return operator<(rhs, lhs); }
friend bool operator <=(const Array& lhs, const Array& rhs) { return !operator>(lhs, rhs); }
friend bool operator >=(const Array& lhs, const Array& rhs) { return !operator<(lhs, rhs); }
Value& operator[](size_t i);
const Value& operator[](size_t i) const;
ValueVector::const_iterator begin() const;
ValueVector::const_iterator end() const;
ValueVector::iterator begin();
ValueVector::iterator end();
// Just a convenience wrapper for doing a std::find(Array::begin(), Array::end(), Value)
ValueVector::iterator find(const Value& v);
ValueVector::const_iterator find(const Value& v) const;
// Convenience wrapper to check if a value is in the array
bool HasValue(const Value& v) const;
// Removes all values and resets the state back to default
void Clear() { mValues.clear(); }
void push_back(const Value& v);
void insert(size_t index, const Value& v);
size_t size() const { return mValues.size(); }
};
class Value
{
protected:
ValueType mValueType = nullptrVal;
int mIntVal = 0;
float mFloatVal = 0;
double mDoubleVal = 0;
std::string mStringVal;
Object mObjectVal;
Array mArrayVal;
bool mBoolVal = false;
public:
const std::string& getStringImplementation() const { return mStringVal; }
Value() = default;
Value(const int v) : mValueType(IntVal), mIntVal(v), mFloatVal(float(v)), mDoubleVal(double(v)) { }
Value(const float v) : mValueType(FloatVal), mIntVal(int(v)), mFloatVal(v), mDoubleVal(double(v)) { }
Value(const double v) : mValueType(DoubleVal), mIntVal(int(v)), mFloatVal(float(v)), mDoubleVal(v) { }
Value(const std::string& v) : mValueType(StringVal), mStringVal(v) { }
Value(const char* v) : mValueType(StringVal), mStringVal(v) { }
Value(const Object& v) : mValueType(ObjectVal), mObjectVal(v) { }
Value(const Array& v) : mValueType(ArrayVal), mArrayVal(v) { }
Value(const bool v) : mValueType(BoolVal), mBoolVal(v) { }
Value(const Value& v);
ValueType GetType() const { return mValueType; }
Value& operator =(const Value& v);
friend bool operator ==(const Value& lhs, const Value& rhs);
friend bool operator !=(const Value& lhs, const Value& rhs) { return !(lhs == rhs); }
friend bool operator <(const Value& lhs, const Value& rhs);
friend bool operator >(const Value& lhs, const Value& rhs) { return operator<(rhs, lhs); }
friend bool operator <=(const Value& lhs, const Value& rhs) { return !operator>(lhs, rhs); }
friend bool operator >=(const Value& lhs, const Value& rhs) { return !operator<(lhs, rhs); }
// For use with Array/ObjectVal types, respectively
Value& operator [](size_t idx);
const Value& operator [](size_t idx) const;
Value& operator [](const std::string& key);
const Value& operator [](const std::string& key) const;
Value& operator [](const char* key);
const Value& operator [](const char* key) const;
bool HasKey(const std::string& key) const;
int HasKeys(const std::vector<std::string>& keys) const;
int HasKeys(const char** keys, int key_count) const;
// non-operator versions
int ToInt() const
{
assert(IsNumeric());
return mIntVal;
}
float ToFloat() const
{
assert(IsNumeric());
return mFloatVal;
}
double ToDouble() const
{
assert(IsNumeric());
return mDoubleVal;
}
bool ToBool() const
{
assert(mValueType == BoolVal);
return mBoolVal;
}
std::string ToString() const
{
assert(mValueType == StringVal);
return mStringVal;
}
Object ToObject() const
{
assert(mValueType == ObjectVal);
return mObjectVal;
}
Array ToArray() const
{
assert(mValueType == ArrayVal);
return mArrayVal;
}
// mutable non-operator versions
Array& ToMutableArray()
{
assert(mValueType == ArrayVal);
return mArrayVal;
}
// 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
// Value to a std::string you can either do:
// my_string = (std::string)my_value
// Or you can now do:
// my_string = my_value.ToString();
//
operator int() const
{
assert(IsNumeric());
return mIntVal;
}
operator float() const
{
assert(IsNumeric());
return mFloatVal;
}
operator double() const
{
assert(IsNumeric());
return mDoubleVal;
}
operator bool() const
{
assert(mValueType == BoolVal);
return mBoolVal;
}
operator std::string const() const
{
assert(mValueType == StringVal);
return mStringVal;
}
operator std::string() const
{
assert(mValueType == StringVal);
return mStringVal;
}
operator const char*() const
{
assert(mValueType == StringVal);
return mStringVal.c_str();
}
operator Object() const
{
assert(mValueType == ObjectVal);
return mObjectVal;
}
operator Array() const
{
assert(mValueType == ArrayVal);
return mArrayVal;
}
bool IsNumeric() const { return (mValueType == IntVal) || (mValueType == DoubleVal) || (mValueType == FloatVal); }
// Returns 1 for anything not an Array/ObjectVal
size_t size() const;
// Resets the state back to default, aka nullptrVal
void Clear() { mValueType = nullptrVal; }
};
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Converts a JSON Object or Array instance into a JSON string representing it.
std::string Serialize(const Value& v);
// If there is an error, Value will be nullptrType
Value Deserialize(const std::string& str);
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
inline bool operator ==(const Object& lhs, const Object& rhs) { return lhs.mValues == rhs.mValues; }
inline bool operator <(const Object& lhs, const Object& rhs) { return lhs.mValues < rhs.mValues; }
inline bool operator ==(const Array& lhs, const Array& rhs) { return lhs.mValues == rhs.mValues; }
inline bool operator <(const Array& lhs, const Array& rhs) { return lhs.mValues < rhs.mValues; }
/* When comparing different numeric types, this method works the same as if you compared different numeric types
on your own. Thus it performs the same as if you, for example, did this:
int a = 1;
float b = 1.1f;
bool equivalent = a == b;
The same logic applies to the other comparison operators.
*/
inline bool operator ==(const Value& lhs, const Value& rhs)
{
if ((lhs.mValueType != rhs.mValueType) && !lhs.IsNumeric() && !rhs.IsNumeric()) { return false; }
switch (lhs.mValueType)
{
case StringVal: return lhs.mStringVal == rhs.mStringVal;
case IntVal:
if (rhs.GetType() == FloatVal) { return float(lhs.mIntVal) == rhs.mFloatVal; }
if (rhs.GetType() == DoubleVal) { return double(lhs.mIntVal) == rhs.mDoubleVal; }
if (rhs.GetType() == IntVal) { return lhs.mIntVal == rhs.mIntVal; }
return false;
case FloatVal:
if (rhs.GetType() == FloatVal) { return lhs.mFloatVal == rhs.mFloatVal; }
if (rhs.GetType() == DoubleVal) { return double(lhs.mFloatVal) == rhs.mDoubleVal; }
if (rhs.GetType() == IntVal) { return lhs.mFloatVal == float(rhs.mIntVal); }
return false;
case DoubleVal:
if (rhs.GetType() == FloatVal) { return lhs.mDoubleVal == double(rhs.mFloatVal); }
if (rhs.GetType() == DoubleVal) { return lhs.mDoubleVal == rhs.mDoubleVal; }
if (rhs.GetType() == IntVal) { return lhs.mDoubleVal == double(rhs.mIntVal); }
return false;
case BoolVal: return lhs.mBoolVal == rhs.mBoolVal;
case ObjectVal: return lhs.mObjectVal == rhs.mObjectVal;
case ArrayVal: return lhs.mArrayVal == rhs.mArrayVal;
default: return true;
}
}
inline bool operator <(const Value& lhs, const Value& rhs)
{
if ((lhs.mValueType != rhs.mValueType) && !lhs.IsNumeric() && !rhs.IsNumeric()) { return false; }
switch (lhs.mValueType)
{
case StringVal: return lhs.mStringVal < rhs.mStringVal;
case IntVal:
if (rhs.GetType() == FloatVal) { return float(lhs.mIntVal) < rhs.mFloatVal; }
if (rhs.GetType() == DoubleVal) { return double(lhs.mIntVal) < rhs.mDoubleVal; }
if (rhs.GetType() == IntVal) { return lhs.mIntVal < rhs.mIntVal; }
return false;
case FloatVal:
if (rhs.GetType() == FloatVal) { return lhs.mFloatVal < rhs.mFloatVal; }
if (rhs.GetType() == DoubleVal) { return double(lhs.mFloatVal) < rhs.mDoubleVal; }
if (rhs.GetType() == IntVal) { return lhs.mFloatVal < float(rhs.mIntVal); }
return false;
case DoubleVal:
if (rhs.GetType() == FloatVal) { return lhs.mDoubleVal < double(rhs.mFloatVal); }
if (rhs.GetType() == DoubleVal) { return lhs.mDoubleVal < rhs.mDoubleVal; }
if (rhs.GetType() == IntVal) { return lhs.mDoubleVal < double(rhs.mIntVal); }
return false;
case BoolVal: return int(lhs.mBoolVal) < int(rhs.mBoolVal);
case ObjectVal: return lhs.mObjectVal < rhs.mObjectVal;
case ArrayVal: return lhs.mArrayVal < rhs.mArrayVal;
default: return true;
}
}
}