635 lines
18 KiB
C++
635 lines
18 KiB
C++
// Copyright (c) 2013-2015 IONU Security, Inc. All rights reserved.
|
|
// Copyright (c) 2016 Sequence Logic, Inc. All rights reserved.
|
|
//
|
|
// JSON utility class
|
|
|
|
#include <cstring>
|
|
|
|
#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<EyeJSONNode*>::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<EyeJSONNode*>::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<EyeJSONNode*>::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<EyeJSONScalar*>(*itr);
|
|
if (replacement) {
|
|
replacement->ReplaceValue (dynamic_cast<EyeJSONScalar*>(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<EyeJSONNode*>::const_iterator itr = _members.begin(); itr != _members.end(); ++itr) {
|
|
if (_type == JSON_ARRAY)
|
|
key = dynamic_cast<EyeJSONScalar*>(*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<EyeJSONNode*>::const_iterator itr = _members.begin(); itr != _members.end(); ++itr)
|
|
delete *itr;
|
|
_members.clear();
|
|
EyeJSONObject* arr = dynamic_cast<EyeJSONObject*>(member);
|
|
for (i = 0; i < arr->GetNumMembers(); ++i) {
|
|
EyeJSONScalar* val = dynamic_cast<EyeJSONScalar*>(arr->GetMember(i));
|
|
AddMember (new EyeJSONScalar (NULL, val->GetValue(), val->GetType()));
|
|
}
|
|
rc = true;
|
|
}
|
|
else if (member->GetType() == JSON_OBJECT) {
|
|
EyeJSONObject* obj = dynamic_cast<EyeJSONObject*>(member);
|
|
for (i = 0; i < obj->GetNumMembers(); ++i) {
|
|
EyeJSONNode* node = obj->GetMember(i);
|
|
EyeJSONScalar* replacement = dynamic_cast<EyeJSONScalar*>(GetMember (node->GetKey()));
|
|
if (replacement && replacement->GetType() == node->GetType()) {
|
|
replacement->ReplaceValue (dynamic_cast<EyeJSONScalar*>(node)->GetValue());
|
|
rc = true;
|
|
}
|
|
else if (!replacement) {
|
|
EyeJSONScalar* item = dynamic_cast<EyeJSONScalar*>(node);
|
|
AddMember (new EyeJSONScalar (item->GetKey(), item->GetValue(), item->GetType()));
|
|
}
|
|
}
|
|
}
|
|
else if (member->GetType() != JSON_NULL) {
|
|
EyeJSONScalar* replacement = dynamic_cast<EyeJSONScalar*>(GetMember (member->GetKey()));
|
|
if (replacement && replacement->GetType() == member->GetType()) {
|
|
replacement->ReplaceValue (dynamic_cast<EyeJSONScalar*>(member)->GetValue());
|
|
rc = true;
|
|
}
|
|
else if (!replacement) {
|
|
EyeJSONScalar* item = dynamic_cast<EyeJSONScalar*>(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<EyeJSONNode*>::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<EyeJSONObject*>(*itr);
|
|
EyeJSONNode* node = container->GetMember (member);
|
|
if (node)
|
|
return node;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void EyeJSONObject::Empty()
|
|
{
|
|
for (std::vector<EyeJSONNode*>::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<EyeJSONNode*>::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;
|
|
}
|