#include "json.h" #include #include #include #include #include #include #include #include #include #include #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(std::isspace)))); // remove trailing white space s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun(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& 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& 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& depth_stack); static Value DeserializeObj(const std::string& _str, std::stack& depth_stack); static Value DeserializeInternal(const std::string& _str, std::stack& 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& 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(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& 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& 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& 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 depth_stack; return DeserializeInternal(str, depth_stack); }