// Copyright (c) 2013-2015 IONU Security, Inc. All rights reserved. // Copyright (c) 2016 Sequence Logic, Inc. All rights reserved. // // JSON utility class #include #include "eyejson.h" #include "eyelog.h" #include "eyeutils.h" using namespace sequencelogic; EyeJSONNode::~EyeJSONNode() { } // Convert JSON escape sequence to value, returns bytes processed and bytes put into value string int EyeJSONNode::ProcessEscape (const char* jsonstr, char* value, int* escbytes) { int rc = 1; *escbytes = 1; unsigned char codepoint[2]; switch (*jsonstr) { case '"' : *value = '"'; break; case '/' : *value = '/'; break; case '\\' : *value = '\\'; break; case 'b' : *value = '\b'; break; case 'f' : *value = '\f'; break; case 'n' : *value = '\n'; break; case 'r' : *value = '\r'; break; case 't' : *value = '\t'; break; case 'u' : // Unicode 4 hex digits jsonstr++; if (sequencelogic::HexToBinary (jsonstr, 4, codepoint)) { //sequencelogic::DumpAsBinary (codepoint, 2); if (codepoint[0] == 0 && codepoint[1] <= 0x7F) { *value++ = (char)codepoint[1]; } else if (codepoint[0] <= 0x7) { // 2 byte UTF-8 *value++ = (char)(0xC0 | (codepoint[0] << 2) | (codepoint[1] >> 6)); *value++ = (char)(0x80 | (codepoint[1] & 0x3F)); *escbytes = 2; } else { // 3 byte UTF-8 *value++ = (char)(0xE0 | (codepoint[0] >> 4)); *value++ = (char)(0x80 | (codepoint[0] << 2) | (codepoint[1] >> 6)); *value++ = (char)(0x80 | (codepoint[1] & 0x3F)); *escbytes = 3; } } else { IONUDEBUG ("EyeJSON(): invalid unicode sequence"); } rc += 4; break; default: IONUDEBUG ("EyeJSON(): invalid escaped character <%c> <<%s>>", *jsonstr, jsonstr-1); break; } return rc; } char* EyeJSONNode::ParseString (const char* str) { if (!str) return NULL; char* retval = new char[strlen(str) + 1]; char* val = retval; int escbytes = 0; while (*str) { if (*str == '\\') { str++; str += ProcessEscape (str, val, &escbytes); val += escbytes; } else *val++ = *str++; } *val = '\0'; return retval; } EyeJSONScalar::EyeJSONScalar (const char* key, const char* value, JSON_TYPE type) { _key = sequencelogic::StrDup (key); //_key = ParseString (key); _value = ParseString (value); _type = type; } EyeJSONScalar::~EyeJSONScalar () { if (_key) delete[] _key; if (_value) delete[] _value; _type = JSON_NULL; _key = NULL; _value = NULL; } void EyeJSONScalar::ReplaceValue (const char* value) { if (sequencelogic::StrCmp (_value, value) == 0) return; // Already set to this value else if (_value) delete[] _value; _value = sequencelogic::StrDup (value); } void EyeJSONScalar::ReplaceKey (const char* key) { if (sequencelogic::StrCmp (_key, key) == 0) return; // Already set to this value else if (_key) delete[] _key; _key = sequencelogic::StrDup (key); } void EyeJSONScalar::GetJSONString (std::string& json) { if (!_value) return; char* vptr = _value; unsigned char codepoint[2]; char hex[5]; switch (_type) { case JSON_STRING : if (_key) { json = "\""; json += _key; json += "\":\""; // key:value pair } else json = "\""; // value if (vptr) { while (*vptr) { switch (*vptr) { case '"' : json += "\\\""; break; case '/' : json += "\\/"; break; case '\\' : json += "\\\\"; break; case '\b' : json += "\\b"; break; case '\f' : json += "\\f"; break; case '\n' : json += "\\n"; break; case '\r' : json += "\\r"; break; case '\t' : json += "\\t"; break; default : // Look for UTF-8 encoded data if ((*vptr & 0x80) == 0x80) { if ((*vptr & 0xe0) == 0xe0) { // 3 byte encoding codepoint[0] = (unsigned char)(*vptr++ << 4); codepoint[0] |= (unsigned char)((*vptr >> 2) & 0x0f); codepoint[1] = (unsigned char)(*vptr++ << 6); codepoint[1] |= (unsigned char)(*vptr & 0x3f); } else if ((*vptr & 0xc0) == 0xc0) { // 2 byte encoding codepoint[0] = (unsigned char)((*vptr >> 2) & 0x7); codepoint[1] = (unsigned char)(*vptr++ << 6); codepoint[1] |= (unsigned char)(*vptr & 0x3f); } else { IONUDEBUG ("Invalid UTF8 encoding"); codepoint[0] = 0; codepoint[1] = 0; } sequencelogic::BinaryToHex (codepoint, 2, hex); json += "\\u"; json += hex; } else json += *vptr; break; } vptr++; } } json += '"'; break; case JSON_BOOL : case JSON_INT : case JSON_FLOAT : case JSON_NULL : if (_key) { json = "\""; json += _key; json += "\":"; json += _value; } else json = _value; break; case JSON_ARRAY : case JSON_OBJECT : break; } } // Create a content object from a JSON str EyeJSONObject::EyeJSONObject (const char* jsonstr) { _key = NULL; _parent = NULL; _type = JSON_OBJECT; bool errflag = false; std::string kbuff = ""; std::string vbuff = ""; JSON_TYPE type = JSON_NULL; EyeJSONNode* jsonValue = NULL; EyeJSONObject* jsonObject = NULL; if (jsonstr) { while (*jsonstr && !errflag) { char c = *jsonstr; switch (c) { case '{' : case '[' : jsonstr++; // Skip '{' or '[' type = (c == '{' ? JSON_OBJECT : JSON_ARRAY); if (jsonObject) { EyeJSONObject* obj = new EyeJSONObject (kbuff.c_str(), type, jsonObject); jsonObject->AddMember (obj); jsonObject = obj; kbuff = ""; } else { jsonObject = this; _type = type; } break; case '}' : case ']' : if (jsonObject && (jsonObject->GetType() == JSON_OBJECT || jsonObject->GetType() == JSON_ARRAY)) { if (jsonObject->GetType() == JSON_ARRAY && kbuff.size() > 0) { jsonValue = new EyeJSONScalar (NULL, kbuff.c_str(), JSON_STRING); jsonObject->AddMember (jsonValue); kbuff = ""; vbuff = ""; } jsonstr++; // Skip '}' or ']' and then done, go back to parent jsonObject = jsonObject->GetParent(); if (jsonObject) type = jsonObject->GetType(); else type = _type; } else { IONUDEBUG ("EyeJSONObject: expecting object/array end"); errflag = true; } break; case 'n' : case 't' : case 'f' : if (c == 't') { jsonstr += 4; type = JSON_BOOL; vbuff = (char*)"true"; } else if (c == 'f') { jsonstr += 5; type = JSON_BOOL; vbuff = (char*)"false"; } else if (c == 'n') { jsonstr += 4; type = JSON_NULL; vbuff = (char*)"null"; } if (jsonObject && (jsonObject->GetType() == JSON_OBJECT || jsonObject->GetType() == JSON_ARRAY)) { jsonValue = new EyeJSONScalar ((kbuff.size() > 0 ? kbuff.c_str() : NULL), vbuff.c_str(), type); jsonObject->AddMember (jsonValue); kbuff = ""; vbuff = ""; } else { IONUDEBUG ("EyeJSONObject: expecting object or array"); errflag = true; } break; case '"' : jsonstr++; // Skip '"' if (kbuff.size() == 0 && type != JSON_ARRAY) { while (*jsonstr) { if (*jsonstr == '\\') { kbuff += *jsonstr++; kbuff += *jsonstr++; } else if (*jsonstr == '"') { jsonstr++; // Skip '"' break; } else kbuff += *jsonstr++; } } else if (vbuff.size() == 0) { while (*jsonstr) { if (*jsonstr == '\\') { vbuff += *jsonstr++; vbuff += *jsonstr++; } else if (*jsonstr == '"') { jsonstr++; // Skip '"' if (jsonObject && (jsonObject->GetType() == JSON_OBJECT || jsonObject->GetType() == JSON_ARRAY)) { jsonValue = new EyeJSONScalar ((kbuff.size() > 0 ? kbuff.c_str() : NULL), vbuff.c_str(), JSON_STRING); jsonObject->AddMember (jsonValue); kbuff = ""; vbuff = ""; } else { IONUDEBUG ("EyeJSONObject: expecting object or array for string"); errflag = true; } break; } else vbuff += *jsonstr++; } } break; case '-' : case '0' : case '1' : case '2' : case '3' : case '4' : case '5' : case '6' : case '7' : case '8' : case '9' : type = JSON_INT; if (jsonObject && (jsonObject->GetType() == JSON_OBJECT || jsonObject->GetType() == JSON_ARRAY)) { while (*jsonstr) { char d = *jsonstr; if (d == '.' || d == 'e' || d == 'E') type = JSON_FLOAT; if (d == ' ' || d == '\t' || d == '\n' || d == ',' || d == ']' || d == '}') { jsonValue = new EyeJSONScalar ((kbuff.size() > 0 ? kbuff.c_str() : NULL), vbuff.c_str(), type); jsonObject->AddMember (jsonValue); kbuff = ""; vbuff = ""; break; } else vbuff += *jsonstr++; } } else { IONUDEBUG ("EyeJSONObject: number <%c> not in object or array", c); errflag = true; } break; case ':' : case ',' : case ' ' : case '\t' : case '\n' : case '\r' : jsonstr++; break; default : // Error //IONUDEBUG ("EyeJSONObject: unexpected character <%c>" , c); errflag = true; break; } } } if (errflag && jsonObject) { if (_key) { delete[] _key; _key = NULL; } for (std::vector::const_iterator itr = _members.begin(); itr != _members.end(); ++itr) delete *itr; _members.clear(); } } EyeJSONObject::EyeJSONObject(const char* key, JSON_TYPE type, EyeJSONObject* parent) { if (key && *key) _key = sequencelogic::StrDup (key); else _key = NULL; _type = type; _parent = parent; } EyeJSONObject::~EyeJSONObject() { if (_key) { delete[] _key; _key = NULL; } for (std::vector::const_iterator itr = _members.begin(); itr != _members.end(); ++itr) delete *itr; _members.clear(); } bool EyeJSONObject::AddMember (EyeJSONNode* member) { if (!member) return false; JSON_TYPE mtype = member->GetType(); for (std::vector::const_iterator itr = _members.begin(); itr != _members.end(); ++itr) { char* key = (*itr)->GetKey(); JSON_TYPE type = (*itr)->GetType(); if (key && sequencelogic::StrCmp (key, member->GetKey()) == 0) { if (mtype == type && type != JSON_OBJECT && type != JSON_ARRAY) { EyeJSONScalar* replacement = dynamic_cast(*itr); if (replacement) { replacement->ReplaceValue (dynamic_cast(member)->GetValue()); return true; } } } } _members.push_back (member); return true; } bool EyeJSONObject::RemoveMember (size_t member) { bool rc = true; if (member < _members.size()) { EyeJSONNode* node = _members[member]; _members.erase (_members.begin() + member); delete node; } else rc = false; return rc; } bool EyeJSONObject::RemoveMember (const char* member) { if (!member) return false; size_t pos = 0; bool found = false; char* key = NULL; for (std::vector::const_iterator itr = _members.begin(); itr != _members.end(); ++itr) { if (_type == JSON_ARRAY) key = dynamic_cast(*itr)->GetValue(); else key = (*itr)->GetKey(); if (sequencelogic::StrCmp (key, member) == 0) { EyeJSONNode* node = _members[pos]; _members.erase (_members.begin() + pos); delete node; found = true; break; } pos++; } return found; } bool EyeJSONObject::UpdateMember (EyeJSONNode* member) { if (!member) return false; size_t i = 0; bool rc = false; if (member) { if (member->GetType() == JSON_ARRAY && GetType() == JSON_ARRAY) { // Clear out old array values for (std::vector::const_iterator itr = _members.begin(); itr != _members.end(); ++itr) delete *itr; _members.clear(); EyeJSONObject* arr = dynamic_cast(member); for (i = 0; i < arr->GetNumMembers(); ++i) { EyeJSONScalar* val = dynamic_cast(arr->GetMember(i)); AddMember (new EyeJSONScalar (NULL, val->GetValue(), val->GetType())); } rc = true; } else if (member->GetType() == JSON_OBJECT) { EyeJSONObject* obj = dynamic_cast(member); for (i = 0; i < obj->GetNumMembers(); ++i) { EyeJSONNode* node = obj->GetMember(i); EyeJSONScalar* replacement = dynamic_cast(GetMember (node->GetKey())); if (replacement && replacement->GetType() == node->GetType()) { replacement->ReplaceValue (dynamic_cast(node)->GetValue()); rc = true; } else if (!replacement) { EyeJSONScalar* item = dynamic_cast(node); AddMember (new EyeJSONScalar (item->GetKey(), item->GetValue(), item->GetType())); } } } else if (member->GetType() != JSON_NULL) { EyeJSONScalar* replacement = dynamic_cast(GetMember (member->GetKey())); if (replacement && replacement->GetType() == member->GetType()) { replacement->ReplaceValue (dynamic_cast(member)->GetValue()); rc = true; } else if (!replacement) { EyeJSONScalar* item = dynamic_cast(member); AddMember (new EyeJSONScalar (item->GetKey(), item->GetValue(), item->GetType())); } } } return rc; } EyeJSONNode* EyeJSONObject::GetMember (size_t member) { if (member < _members.size()) return _members[member]; else return NULL; } EyeJSONNode* EyeJSONObject::GetMember (const char* member) { if (!member) return NULL; for (std::vector::const_iterator itr = _members.begin(); itr != _members.end(); ++itr) { char* key = (*itr)->GetKey(); JSON_TYPE type = (*itr)->GetType(); if (sequencelogic::StrCmp (key, member) == 0) return *itr; if (type == JSON_ARRAY || type == JSON_OBJECT) { EyeJSONObject* container = dynamic_cast(*itr); EyeJSONNode* node = container->GetMember (member); if (node) return node; } } return NULL; } void EyeJSONObject::Empty() { for (std::vector::const_iterator itr = _members.begin(); itr != _members.end(); ++itr) delete *itr; _members.clear(); } void EyeJSONObject::ReplaceKey (const char* key) { if (sequencelogic::StrCmp (_key, key) == 0) return; // Already set to this value else if (_key) delete[] _key; _key = sequencelogic::StrDup (key); } void EyeJSONObject::GetJSONString (std::string& json) { if (_type == JSON_OBJECT) { if (_key && *_key && _parent) { json = "\""; json += _key; json += "\":{"; } else json = "{"; } else { if (_key && *_key && _parent) { json = "\""; json += _key; json += "\":["; } else json = "["; } for (std::vector::const_iterator itr = _members.begin(); itr != _members.end(); ++itr) { if (itr != _members.begin()) json += ","; std::string mjson; (*itr)->GetJSONString (mjson); json += mjson; } if (_type == JSON_OBJECT) json += "}"; else json += "]"; } std::string EyeJSONObject::Stringify () { std::string json; GetJSONString (json); return json; }