#include "gb_json.h" #include "cJSON.h" #include #include #include namespace special_char_trans { struct { const char* writedown_text; char readable_char; }transferred_chars[] = { { "\\\"", '\"' } , { "\\'", '\'' } , { "\\a", '\a' } , { "\\b", '\b' } , { "\\f", '\f' } , { "\\n", '\n' } , { "\\r", '\r' } , { "\\t", '\t' } , { "\\v", '\v' } // , { "\\?", '\?' } , { "\\\\", '\\' } , { "\\/", '/' } // , { "\\0", '\0' } }; void to_writedown(std::string& str) { std::string trans(str); const char* ptr = trans.c_str(); str.clear(); while (*ptr) { bool rep = false; if (*ptr == '\\') { if (ptr[1] == '\\') { str += "\\\\"; ptr++; rep = true; } else if( ptr[1] == '/' || ptr[1] == 'a' || ptr[1] == 'b' || ptr[1] == 'f' || ptr[1] == 'n' || ptr[1] == 'r' || ptr[1] == 't' || ptr[1] == 'u' || ptr[1] == 'v') { str += "\\"; ptr++; } else { str += "\\\\"; rep = true; } } else { for (size_t i = 0; i < sizeof(transferred_chars) / sizeof(transferred_chars[0]); ++i) { if (*ptr == transferred_chars[i].readable_char) { str += transferred_chars[i].writedown_text; rep = true; break; } } } if (!rep) str.append(1, *ptr); ptr++; } } } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // life callback ... #ifdef DUMP_JSON_OBJECT_LIFE static void record_life_empty(gb_json*, bool, void*) {} static void(*g_life)(gb_json*, bool, void*) = &record_life_empty; static void* g_life_param = nullptr; void set_gbjson_life_callback(void(*life)(gb_json*, bool, void*), void* param) { g_life_param = param; g_life = life ? life : &record_life_empty; } #endif ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // gb_json ... gb_json::gb_json(char* json_txt) : ref_(1), type_(VAL_TYPE_OBJECT), key_(""), strval_(""), cur_child_(-1) { #ifdef DUMP_JSON_OBJECT_LIFE g_life(this, true, g_life_param); #endif simple_val_.dval = .0f; if(json_txt) attach_text(json_txt); } gb_json::gb_json(const char* key, bool val) : ref_(1), type_(VAL_TYPE_BOOL), key_(key ? key : ""), strval_(""), cur_child_(-1) { #ifdef DUMP_JSON_OBJECT_LIFE g_life(this, true, g_life_param); #endif simple_val_.bval = val; } gb_json::gb_json(const char* key, int val) : ref_(1), type_(VAL_TYPE_INT), key_(key ? key : ""), strval_(""), cur_child_(-1) { #ifdef DUMP_JSON_OBJECT_LIFE g_life(this, true, g_life_param); #endif simple_val_.nval = val; } gb_json::gb_json(const char* key, double val) : ref_(1), type_(VAL_TYPE_FLOAT), key_(key ? key : ""), strval_(""), cur_child_(-1) { #ifdef DUMP_JSON_OBJECT_LIFE g_life(this, true, g_life_param); #endif simple_val_.dval = val; } gb_json::gb_json(const char* key, const char* val) : ref_(1), type_(VAL_TYPE_STRING), key_(key ? key : ""), strval_(val ? val : ""), cur_child_(-1) { #ifdef DUMP_JSON_OBJECT_LIFE g_life(this, true, g_life_param); #endif } gb_json::gb_json(const char* key, const std::string& val) : ref_(1), type_(VAL_TYPE_STRING), key_(key ? key : ""), strval_(val), cur_child_(-1) { #ifdef DUMP_JSON_OBJECT_LIFE g_life(this, true, g_life_param); #endif } gb_json::~gb_json() { clear(); #ifdef DUMP_JSON_OBJECT_LIFE g_life(this, false, g_life_param); #endif } std::string gb_json::object_key(gb_json* jsn) { return "\"" + jsn->key() + "\":"; } std::string gb_json::array_key(gb_json* jsn) { return ""; } void gb_json::from_cjson(cJSON* cj) { key_ = cj && cj->string ? cj->string : ""; while (cj) { gb_json* child = nullptr; if (cj->type == cJSON_True) { child = new gb_json(cj->string, true); } else if(cj->type == cJSON_False) { child = new gb_json(cj->string, false); } else if (cj->type == cJSON_Number) { if (cj->valuedouble - (int)cj->valuedouble < .00001) { child = new gb_json(cj->string, cj->valueint); } else { child = new gb_json(cj->string, cj->valuedouble); } } else if (cj->type == cJSON_String) { child = new gb_json(cj->string, cj->valuestring); } else if (cj->type == cJSON_Object || cj->type == cJSON_Array) { child = new gb_json(); child->from_cjson(cj->child); child->key_ = cj->string ? cj->string : ""; if (child->key_.empty()) child->type_ = VAL_TYPE_OBJECT; } arr_val_.push_back(child); cj = cj->next; } //if (arr_val_.size() == 1 && arr_val_[0]->arr_val_.size() == 0) //{ // gb_json* child = arr_val_[0]; // // if (!child->key_.empty()) // array // { // arr_val_.clear(); // type_ = child->type_; // key_ = child->key_; // simple_val_.dval = child->simple_val_.dval; // strval_ = child->strval_; // for (auto& v : child->arr_val_) // arr_val_.push_back(v); // child->arr_val_.clear(); // child->release(); // } //} if (arr_val_.size()) { type_ = arr_val_[0]->key().empty() ? VAL_TYPE_ARRAY : VAL_TYPE_OBJECT; } } gb_json* gb_json::find_child(const char* key, bool remove) { gb_json* ret = nullptr; if (type_ == VAL_TYPE_OBJECT) { for (size_t i = 0; i < arr_val_.size(); ++i) { if (arr_val_[i]->key() == key) { ret = arr_val_[i]; if (remove) arr_val_.erase(arr_val_.begin() + i); else ret->add_ref(); break; } } } return ret; } int32_t gb_json::add_ref() { std::lock_guard lock(ref_mutex_); int32_t ref = ++ref_; return ref; } int32_t gb_json::release() { int32_t ref = 0; { std::lock_guard lock(ref_mutex_); ref = --ref_; } if (ref == 0) delete this; return ref; } static void check_cJSON(cJSON* root, std::vector* existing) { if (!root) return; for (auto& v : *existing) { if (v == root) { printf("cJSON* 0x%08x is repeat!\n", v); break; } } existing->push_back(root); check_cJSON(root->child, existing); cJSON* next = root->next; while (next) { check_cJSON(next, existing); next = next->next; } } bool gb_json::attach_text(char* json_txt) { clear(); cJSON* jsn = cJSON_Parse(json_txt); if (jsn) { char *text = cJSON_Print(jsn); if (text) { if (0) { FILE* dst = fopen("e:\\test-json.txt", "wb"); fwrite(text, 1, strlen(text), dst); fclose(dst); } free(text); } std::vector repeat; if(0) check_cJSON(jsn, &repeat); from_cjson(jsn->child); cJSON_Delete(jsn); return true; } return false; } void gb_json::clear(bool as_array) { if (type_ == VAL_TYPE_ARRAY || type_ == VAL_TYPE_OBJECT) { for (auto& v : arr_val_) v->release(); } type_ = as_array ? VAL_TYPE_ARRAY : VAL_TYPE_OBJECT; simple_val_.dval = .0f; key_ = ""; strval_ = ""; arr_val_.clear(); cur_child_ = -1; } std::string gb_json::to_string(void) { if (type_ == VAL_TYPE_NULL) return ""; if (type_ == VAL_TYPE_BOOL) return (simple_val_.bval ? "true" : "false"); if (type_ == VAL_TYPE_INT) return std::to_string(simple_val_.nval); if (type_ == VAL_TYPE_FLOAT) return std::to_string(simple_val_.dval); if (type_ == VAL_TYPE_STRING) { char* u = cJSON_utf8_2_unic(strval_.c_str()); std::string r(u); free(u); special_char_trans::to_writedown(r); return "\"" + r + "\""; } std::string(*k)(gb_json*) = type_ == VAL_TYPE_OBJECT ? gb_json::object_key : gb_json::array_key; std::string str(type_ == VAL_TYPE_OBJECT ? "{" : "["); if (arr_val_.size()) { str += k(arr_val_[0]) + arr_val_[0]->to_string(); for(size_t i = 1; i < arr_val_.size(); ++i) str += "," + k(arr_val_[i]) + arr_val_[i]->to_string(); } str += type_ == VAL_TYPE_OBJECT ? "}" : "]"; return str; } std::string& gb_json::key(void) { return key_; } bool gb_json::is_array(void) { return type_ == VAL_TYPE_ARRAY; } bool gb_json::is_leaf_node(void) { return type_ == VAL_TYPE_BOOL || type_ == VAL_TYPE_INT || type_ == VAL_TYPE_FLOAT || type_ == VAL_TYPE_STRING; } bool gb_json::get_value(const char* key, bool& val) { bool ret = false; gb_json* child = find_child(key); if (child) { if (child->type_ == VAL_TYPE_BOOL) { val = child->simple_val_.bval; ret = true; } child->release(); } else if (type_ == VAL_TYPE_BOOL && key_ == key) { val = simple_val_.bval; ret = true; } return ret; } bool gb_json::get_value(const char* key, int& val) { bool ret = false; gb_json* child = find_child(key); if (child) { if (child->type_ == VAL_TYPE_INT) { val = child->simple_val_.nval; ret = true; } child->release(); } else if (type_ == VAL_TYPE_INT && key_ == key) { val = simple_val_.nval; ret = true; } return ret; } bool gb_json::get_value(const char* key, double& val) { bool ret = false; gb_json* child = find_child(key); if (child) { if (child->type_ == VAL_TYPE_FLOAT) { val = child->simple_val_.dval; ret = true; } child->release(); } else if (type_ == VAL_TYPE_FLOAT && key_ == key) { val = simple_val_.dval; ret = true; } // added on 2023-04-27: for cJSON consider both int and float as CJSON_Number, we consider int if the value is just an integer if(!ret) { int v = 0; ret = get_value(key, v); if(ret) val = v; } return ret; } bool gb_json::get_value(const char* key, std::string& val) { bool ret = false; gb_json* child = find_child(key); if (child) { if (child->type_ == VAL_TYPE_STRING) { val = child->strval_; ret = true; } child->release(); } else if (type_ == VAL_TYPE_STRING && key_ == key) { val = strval_; ret = true; } return ret; } bool gb_json::get_value(const char* key, gb_json*& val) { bool ret = false; gb_json *child = find_child(key); if (child) { if (child->type_ == VAL_TYPE_OBJECT || child->type_ == VAL_TYPE_ARRAY) { val = child; ret = true; } else { child->release(); } } return ret; } size_t gb_json::children(void) { if (type_ == VAL_TYPE_ARRAY || type_ == VAL_TYPE_OBJECT) return arr_val_.size(); else return -1; } gb_json* gb_json::child(size_t ind) { if (type_ == VAL_TYPE_ARRAY || type_ == VAL_TYPE_OBJECT) { if (ind >= 0 && ind < arr_val_.size()) { arr_val_[ind]->add_ref(); return arr_val_[ind]; } } return nullptr; } gb_json* gb_json::first_child(void) { if (type_ == VAL_TYPE_OBJECT || type_ == VAL_TYPE_ARRAY) { cur_child_ = 0; if (arr_val_.size()) { arr_val_[0]->add_ref(); return arr_val_[0]; } } return nullptr; } gb_json* gb_json::next_child(void) { if (type_ == VAL_TYPE_OBJECT || type_ == VAL_TYPE_ARRAY) { if (++cur_child_ < arr_val_.size()) { arr_val_[cur_child_]->add_ref(); return arr_val_[cur_child_]; } } return nullptr; } bool gb_json::set_value(const char* key, bool val) { if (type_ != VAL_TYPE_OBJECT) return false; gb_json* child = find_child(key); if (child) { child->clear(); child->type_ = VAL_TYPE_BOOL; child->key() = key ? key : ""; child->simple_val_.bval = val; child->release(); } else { child = new gb_json(key, val); arr_val_.push_back(child); } return true; } bool gb_json::set_value(const char* key, int val) { if (type_ != VAL_TYPE_OBJECT) return false; gb_json* child = find_child(key); if (child) { child->clear(); child->type_ = VAL_TYPE_INT; child->key() = key ? key : ""; child->simple_val_.nval = val; child->release(); } else { child = new gb_json(key, val); arr_val_.push_back(child); } return true; } bool gb_json::set_value(const char* key, double val) { if (type_ != VAL_TYPE_OBJECT) return false; gb_json* child = find_child(key); if (child) { child->clear(); child->type_ = VAL_TYPE_FLOAT; child->key() = key ? key : ""; child->simple_val_.dval = val; child->release(); } else { child = new gb_json(key, val); arr_val_.push_back(child); } return true; } bool gb_json::set_value(const char* key, const char* val) { if (type_ != VAL_TYPE_OBJECT) return false; gb_json* child = find_child(key); if (child) { child->clear(); child->type_ = VAL_TYPE_STRING; child->key() = key ? key : ""; child->strval_ = val ? val : ""; child->release(); } else { child = new gb_json(key, val); arr_val_.push_back(child); } return true; } bool gb_json::set_value(const char* key, gb_json* val) { if (type_ != VAL_TYPE_OBJECT) return false; for (size_t i = 0; i < arr_val_.size(); ++i) { if (arr_val_[i]->key() == key) { arr_val_[i]->release(); arr_val_[i] = val; val->add_ref(); return true; } } arr_val_.push_back(val); val->key() = key; val->add_ref(); return true; } gb_json& gb_json::operator+=(bool val) { if (type_ == VAL_TYPE_ARRAY) { gb_json* child = new gb_json(nullptr, val); arr_val_.push_back(child); } return *this; } gb_json& gb_json::operator+=(int val) { if (type_ == VAL_TYPE_ARRAY) { gb_json* child = new gb_json(nullptr, val); arr_val_.push_back(child); } return *this; } gb_json& gb_json::operator+=(double val) { if (type_ == VAL_TYPE_ARRAY) { gb_json* child = new gb_json(nullptr, val); arr_val_.push_back(child); } return *this; } gb_json& gb_json::operator+=(const char* val) { if (type_ == VAL_TYPE_ARRAY) { gb_json* child = new gb_json(nullptr, val); arr_val_.push_back(child); } return *this; } gb_json& gb_json::operator+=(gb_json* val) { if (type_ == VAL_TYPE_ARRAY) { val->add_ref(); arr_val_.push_back(val); } return *this; } gb_json& gb_json::operator-=(int ind) { remove(ind); return *this; } bool gb_json::remove(const char* key) { gb_json* child = find_child(key, true); if (child) { child->release(); return true; } else { return false; } } bool gb_json::remove(gb_json* child) { if (type_ == VAL_TYPE_ARRAY || type_ == VAL_TYPE_OBJECT) { for (size_t i = 0; i < arr_val_.size(); ++i) { if (arr_val_[i] == child) { arr_val_[i]->release(); arr_val_.erase(arr_val_.begin() + i); return true; } } } return false; } bool gb_json::remove(int ind) { bool ret = false; if (type_ == VAL_TYPE_ARRAY || type_ == VAL_TYPE_OBJECT) { if (ind >= 0 && ind < arr_val_.size()) { arr_val_[ind]->release(); arr_val_.erase(arr_val_.begin() + ind); ret = true; } } return ret; } int gb_json::index(gb_json* child) { if (type_ == VAL_TYPE_ARRAY || type_ == VAL_TYPE_OBJECT) { for (int i = 0; i < arr_val_.size(); ++i) { if (arr_val_[i] == child) return i; } } return -1; } int gb_json::index_move_to(gb_json* child, int ind) { int i = index(child); if (i == -1) return -1; arr_val_.erase(arr_val_.begin() + i); if (ind < 0) ind = 0; if (ind > arr_val_.size()) ind = arr_val_.size(); arr_val_.insert(arr_val_.begin() + ind, child); return ind; } int gb_json::insert(int ind, const char* key, gb_json* child) { int i = index(child); if (i == -1) { if (ind < 0) ind = 0; else if (ind > arr_val_.size()) ind = arr_val_.size(); child->key() = key ? key : ""; arr_val_.insert(arr_val_.begin() + ind, child); child->add_ref(); } else if(i != ind) { arr_val_.erase(arr_val_.begin() + i); if (ind < 0) ind = 0; if (ind > arr_val_.size()) ind = arr_val_.size(); child->key() = key ? key : ""; arr_val_.insert(arr_val_.begin() + ind, child); } return ind; } bool gb_json::value(bool& val) { bool ret = false; if (is_leaf_node() && type_ == VAL_TYPE_BOOL) { val = simple_val_.bval; ret = true; } return ret; } bool gb_json::value(int& val) { bool ret = false; if (is_leaf_node() && type_ == VAL_TYPE_INT) { val = simple_val_.nval; ret = true; } return ret; } bool gb_json::value(double& val) { bool ret = false; if (is_leaf_node() && type_ == VAL_TYPE_FLOAT) { val = simple_val_.dval; ret = true; } return ret; } bool gb_json::value(std::string& val) { bool ret = false; if (is_leaf_node() && type_ == VAL_TYPE_STRING) { val = strval_; ret = true; } return ret; } gb_json& gb_json::operator=(bool val) { if (is_leaf_node()) { simple_val_.bval = val; type_ = VAL_TYPE_BOOL; } return *this; } gb_json& gb_json::operator=(int val) { if (is_leaf_node()) { simple_val_.nval = val; type_ = VAL_TYPE_INT; } return *this; } gb_json& gb_json::operator=(double val) { if (is_leaf_node()) { simple_val_.dval = val; type_ = VAL_TYPE_FLOAT; } return *this; } gb_json& gb_json::operator=(const char* val) { if (is_leaf_node()) { strval_ = val ? val : ""; type_ = VAL_TYPE_STRING; } return *this; } bool gb_json::operator==(const gb_json& r) { if (type_ != r.type_) return false; if (type_ == VAL_TYPE_BOOL) return simple_val_.bval == r.simple_val_.bval; if (type_ == VAL_TYPE_INT) return simple_val_.nval == r.simple_val_.nval; if (type_ == VAL_TYPE_FLOAT) return fabs(simple_val_.dval - r.simple_val_.dval) < .00001; if (type_ == VAL_TYPE_STRING) return strval_ == r.strval_; if (arr_val_.size() != r.arr_val_.size()) return false; for (int i = 0; i < arr_val_.size(); ++i) { if (!(*arr_val_[i] == *r.arr_val_[i])) return false; } return true; } bool gb_json::operator!=(const gb_json& r) { return !(*this == r); } bool gb_json::revise_number_type(bool dbval) { bool chg = false; if (dbval) { // int -> double if (type_ == VAL_TYPE_INT) { type_ = VAL_TYPE_FLOAT; simple_val_.dval = simple_val_.nval; chg = true; } else chg = type_ == VAL_TYPE_FLOAT; } else { // double -> int if (type_ == VAL_TYPE_FLOAT) { type_ = VAL_TYPE_INT; simple_val_.nval = simple_val_.dval + .5f; chg = true; } else chg = type_ == VAL_TYPE_INT; } return chg; }