init
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user