1432 lines
30 KiB
C++
1432 lines
30 KiB
C++
/***************************************************************************
|
|
* Copyright (C) 2023-, openlinyou, <linyouhappy@outlook.com>
|
|
*
|
|
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
|
* copies of the Software, and permit persons to whom the Software is
|
|
* furnished to do so, under the terms of the COPYING file.
|
|
***************************************************************************/
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
#include <stdio.h>
|
|
#include <string>
|
|
#include <vector>
|
|
#include <memory.h>
|
|
#include <stdlib.h>
|
|
#include <assert.h>
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
#include <math.h>
|
|
#include <stdarg.h>
|
|
#include <string>
|
|
|
|
#include "openjson.h"
|
|
|
|
|
|
#define PRINTF printf
|
|
#if (defined(_MSC_VER) && (_MSC_VER >= 1400 ))
|
|
inline int SNPRINTF(char* buffer, size_t size, const char* format, ...)
|
|
{
|
|
va_list va;
|
|
va_start(va, format);
|
|
int result = vsnprintf_s(buffer, size, _TRUNCATE, format, va);
|
|
va_end(va);
|
|
return result;
|
|
}
|
|
#define SSCANF sscanf_s
|
|
#else
|
|
#define SNPRINTF snprintf
|
|
#define SSCANF sscanf
|
|
#endif
|
|
|
|
static inline void doubleToStr(double v, char* buffer, int size)
|
|
{
|
|
double tmp = floor(v);
|
|
if (tmp == v)
|
|
SNPRINTF(buffer, size, "%ld", (long)v);
|
|
else
|
|
SNPRINTF(buffer, size, "%g", v);
|
|
}
|
|
|
|
static inline bool strToDouble(const char* str, double* value)
|
|
{
|
|
return SSCANF(str, "%lf", value) == 1 ? true : false;
|
|
}
|
|
|
|
static void int32ToStr(int32_t n, char* str, size_t size)
|
|
{
|
|
if (str == 0 || size < 1) return;
|
|
str[size - 1] = 0;
|
|
if (size < 2) return;
|
|
if (n == 0)
|
|
{
|
|
str[0] = '0';
|
|
return;
|
|
}
|
|
size_t i = 0;
|
|
char buf[128] = { 0 };
|
|
int32_t tmp = n < 0 ? -n : n;
|
|
while (tmp && i < 128)
|
|
{
|
|
buf[i++] = (tmp % 10) + '0';
|
|
tmp = tmp / 10;
|
|
}
|
|
size_t len = n < 0 ? ++i : i;
|
|
if (len > size)
|
|
{
|
|
len = size;
|
|
i = len - 1;
|
|
}
|
|
str[i] = 0;
|
|
while (1)
|
|
{
|
|
--i;
|
|
if (i < 0 || buf[len - i - 1] == 0) break;
|
|
str[i] = buf[len - i - 1];
|
|
}
|
|
if (i == 0) str[i] = '-';
|
|
}
|
|
|
|
static void int64ToStr(int64_t n, char* str, size_t size)
|
|
{
|
|
if (str == 0 || size < 1) return;
|
|
str[size - 1] = 0;
|
|
if (size < 2) return;
|
|
if (n == 0)
|
|
{
|
|
str[0] = '0';
|
|
return;
|
|
}
|
|
size_t i = 0;
|
|
char buf[128] = { 0 };
|
|
int64_t tmp = n < 0 ? -n : n;
|
|
while (tmp && i < 128)
|
|
{
|
|
buf[i++] = (tmp % 10) + '0';
|
|
tmp = tmp / 10;
|
|
}
|
|
size_t len = n < 0 ? ++i : i;
|
|
if (len > size)
|
|
{
|
|
len = size;
|
|
i = len - 1;
|
|
}
|
|
str[i] = 0;
|
|
while (1)
|
|
{
|
|
--i;
|
|
if (i < 0 || buf[len - i - 1] == 0) break;
|
|
str[i] = buf[len - i - 1];
|
|
}
|
|
if (i == 0) str[i] = '-';
|
|
}
|
|
|
|
static int32_t strToInt32(const char* str)
|
|
{
|
|
const char* ptr = str;
|
|
if (*ptr == '-' || *ptr == '+') ptr++;
|
|
int32_t tmp = 0;
|
|
while (*ptr != 0)
|
|
{
|
|
if ((*ptr < '0') || (*ptr > '9')) break;
|
|
tmp = tmp * 10 + (*ptr - '0');
|
|
ptr++;
|
|
}
|
|
if (*str == '-') tmp = -tmp;
|
|
return tmp;
|
|
}
|
|
|
|
static int64_t strToInt64(const char* str)
|
|
{
|
|
const char* ptr = str;
|
|
if (*ptr == '-' || *ptr == '+') ptr++;
|
|
int64_t temp = 0;
|
|
while (*ptr != 0)
|
|
{
|
|
if ((*ptr < '0') || (*ptr > '9')) break;
|
|
temp = temp * 10 + (*ptr - '0');
|
|
ptr++;
|
|
}
|
|
if (*str == '-') temp = -temp;
|
|
return temp;
|
|
}
|
|
|
|
|
|
//JsonBox
|
|
OpenJson::Box::Box()
|
|
{
|
|
}
|
|
|
|
OpenJson::Box::~Box()
|
|
{
|
|
for (size_t i = 0; i < childs_.size(); i++)
|
|
{
|
|
if (childs_[i])
|
|
{
|
|
delete childs_[i];
|
|
}
|
|
}
|
|
childs_.clear();
|
|
}
|
|
|
|
bool OpenJson::Box::remove(OpenJson* node)
|
|
{
|
|
if (!node) return false;
|
|
std::vector<OpenJson*>::iterator iter;
|
|
for (iter = childs_.begin(); iter != childs_.end(); iter++)
|
|
{
|
|
if (*iter == node)
|
|
{
|
|
childs_.erase(iter);
|
|
delete node;
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//JsonContext
|
|
OpenJson::Context::Context()
|
|
:
|
|
data_(0),
|
|
size_(0),
|
|
offset_(0),
|
|
root_(0)
|
|
{
|
|
}
|
|
|
|
OpenJson::Context::~Context()
|
|
{
|
|
}
|
|
|
|
void OpenJson::Context::startRead()
|
|
{
|
|
size_ = rbuffer_.size();
|
|
data_ = (char*)rbuffer_.data();
|
|
offset_ = 0;
|
|
}
|
|
|
|
void OpenJson::Context::startWrite()
|
|
{
|
|
wbuffer_.clear();
|
|
}
|
|
|
|
//Segment
|
|
OpenJson::Segment::Segment(SegmentType type)
|
|
{
|
|
setType(type);
|
|
}
|
|
OpenJson::Segment::~Segment()
|
|
{
|
|
}
|
|
void OpenJson::Segment::setType(SegmentType type)
|
|
{
|
|
type_ = type;
|
|
value_.int64_ = 0;
|
|
}
|
|
void OpenJson::Segment::clear()
|
|
{
|
|
value_.int64_ = 0;
|
|
}
|
|
|
|
void OpenJson::Segment::toString()
|
|
{
|
|
switch (type_)
|
|
{
|
|
case NIL:
|
|
content_ = "null";
|
|
break;
|
|
case BOOL:
|
|
content_ = value_.bool_ ? "true" : "false";
|
|
break;
|
|
case INT32:
|
|
{
|
|
char buffer[64] = { 0 };
|
|
int32ToStr(value_.int32_, buffer, sizeof(buffer));
|
|
content_ = buffer;
|
|
}
|
|
break;
|
|
case INT64:
|
|
{
|
|
char buffer[64] = { 0 };
|
|
int64ToStr(value_.int64_, buffer, sizeof(buffer));
|
|
content_ = buffer;
|
|
}
|
|
break;
|
|
case DOUBLE:
|
|
{
|
|
char buffer[64] = { 0 };
|
|
doubleToStr(value_.double_, buffer, sizeof(buffer));
|
|
content_ = buffer;
|
|
}
|
|
break;
|
|
case STRING:
|
|
break;
|
|
default:
|
|
content_.clear();
|
|
break;
|
|
}
|
|
}
|
|
|
|
//OpenJson
|
|
bool OpenJson::EnableLog_ = true;
|
|
|
|
OpenJson OpenJson::NodeNull;
|
|
std::string OpenJson::StringNull;
|
|
|
|
OpenJson::OpenJson(JsonType type)
|
|
:type_(type),
|
|
context_(0),
|
|
wcontext_(0),
|
|
idx_(0),
|
|
len_(0),
|
|
box_(0),
|
|
key_(0),
|
|
segment_(0)
|
|
{
|
|
}
|
|
|
|
OpenJson::~OpenJson()
|
|
{
|
|
clear();
|
|
}
|
|
|
|
OpenJson* OpenJson::createNode(unsigned char code)
|
|
{
|
|
JsonType ctype = UNKNOWN;
|
|
switch (code)
|
|
{
|
|
case '"':
|
|
case '\'':
|
|
ctype = STRING;
|
|
break;
|
|
case '{':
|
|
ctype = OBJECT;
|
|
break;
|
|
case '[':
|
|
ctype = ARRAY;
|
|
break;
|
|
default:
|
|
ctype = NUMBER;
|
|
break;
|
|
}
|
|
OpenJson* node = new OpenJson(ctype);
|
|
return node;
|
|
}
|
|
|
|
OpenJson::JsonType OpenJson::codeToType(unsigned char code)
|
|
{
|
|
JsonType ctype = UNKNOWN;
|
|
switch (code)
|
|
{
|
|
case '"':
|
|
case '\'':
|
|
ctype = STRING;
|
|
break;
|
|
case '{':
|
|
ctype = OBJECT;
|
|
break;
|
|
case '[':
|
|
ctype = ARRAY;
|
|
break;
|
|
default:
|
|
ctype = NUMBER;
|
|
break;
|
|
}
|
|
return ctype;
|
|
}
|
|
|
|
const std::string& OpenJson::emptyString()
|
|
{
|
|
if (context_)
|
|
{
|
|
context_->stringNull_.clear();
|
|
return context_->stringNull_;
|
|
}
|
|
if (wcontext_)
|
|
{
|
|
wcontext_->stringNull_.clear();
|
|
return wcontext_->stringNull_;
|
|
}
|
|
return OpenJson::StringNull;
|
|
}
|
|
|
|
const std::string& OpenJson::key()
|
|
{
|
|
if (key_) return key_->s();
|
|
return emptyString();
|
|
}
|
|
|
|
const char* OpenJson::data()
|
|
{
|
|
if (context_ && context_->data_)
|
|
{
|
|
if (idx_ < context_->size_)
|
|
{
|
|
return context_->data_ + idx_;
|
|
}
|
|
}
|
|
Log("JsonNode is Empty");
|
|
return emptyString().c_str();
|
|
}
|
|
|
|
double OpenJson::stringToDouble()
|
|
{
|
|
const char* str = data();
|
|
double dval = 0;
|
|
if (!str || strlen(str) == 0)
|
|
dval = (float)(1e+300 * 1e+300) * 0.0F;
|
|
else if (strcmp(str, "true") == 0)
|
|
dval = 1.0;
|
|
else if (strcmp(str, "false") == 0)
|
|
dval = 0.0;
|
|
else
|
|
dval = atof(str);
|
|
return dval;
|
|
}
|
|
|
|
int32_t OpenJson::stringToInt32()
|
|
{
|
|
int32_t ret = atoi(data());
|
|
return ret;
|
|
}
|
|
|
|
int64_t OpenJson::stringToInt64()
|
|
{
|
|
int64_t ret = atoll(data());
|
|
return ret;
|
|
}
|
|
|
|
const std::string& OpenJson::s()
|
|
{
|
|
if (type_ == STRING)
|
|
{
|
|
if (!segment_)
|
|
{
|
|
segment_ = new Segment(Segment::STRING);
|
|
segment_->content_ = data();
|
|
}
|
|
if (segment_->type_ == Segment::STRING)
|
|
{
|
|
return segment_->content_;
|
|
}
|
|
segment_->toString();
|
|
return segment_->content_;
|
|
}
|
|
else if (type_ == NUMBER)
|
|
{
|
|
Log("JsonNode is no STRING");
|
|
if (!segment_)
|
|
{
|
|
if (!context_ || !context_->data_ || len_ < 1)
|
|
{
|
|
return emptyString();
|
|
}
|
|
segment_ = new Segment(Segment::NIL);
|
|
segment_->content_ = data();
|
|
return segment_->content_;
|
|
}
|
|
if (segment_)
|
|
{
|
|
if (segment_->type_ != Segment::NIL)
|
|
{
|
|
segment_->toString();
|
|
}
|
|
return segment_->content_;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Log("JsonNode is no STRING");
|
|
}
|
|
return emptyString();
|
|
}
|
|
|
|
double OpenJson::d(double def)
|
|
{
|
|
if (type_ != NUMBER)
|
|
{
|
|
Log("JsonNode is no NUMBER");
|
|
return def;
|
|
}
|
|
if (segment_ == 0)
|
|
{
|
|
if (!context_ || !context_->data_ || len_ < 1)
|
|
{
|
|
return def;
|
|
}
|
|
segment_ = new Segment(Segment::DOUBLE);
|
|
segment_->value_.double_ = stringToDouble();
|
|
}
|
|
if (segment_->type_ != Segment::DOUBLE)
|
|
{
|
|
if (!context_ || !context_->data_ || len_ < 1)
|
|
{
|
|
Log("JsonNode is no DOUBLE NUMBER");
|
|
}
|
|
else
|
|
{
|
|
segment_->setType(Segment::DOUBLE);
|
|
segment_->value_.double_ = stringToDouble();
|
|
}
|
|
}
|
|
switch (segment_->type_)
|
|
{
|
|
case OpenJson::Segment::BOOL:
|
|
return segment_->value_.bool_;
|
|
case OpenJson::Segment::INT32:
|
|
return (double)segment_->value_.int32_;
|
|
case OpenJson::Segment::INT64:
|
|
return (double)segment_->value_.int64_;
|
|
case OpenJson::Segment::DOUBLE:
|
|
return segment_->value_.double_;
|
|
case OpenJson::Segment::STRING:
|
|
return atof(segment_->content_.c_str());
|
|
default:
|
|
break;
|
|
}
|
|
return def;
|
|
}
|
|
|
|
bool OpenJson::b(bool def)
|
|
{
|
|
if (type_ != NUMBER)
|
|
{
|
|
Log("JsonNode is no NUMBER");
|
|
return def;
|
|
}
|
|
if (segment_ == 0)
|
|
{
|
|
if (!context_ || !context_->data_ || len_ < 1)
|
|
{
|
|
return def;
|
|
}
|
|
segment_ = new Segment(Segment::BOOL);
|
|
segment_->value_.bool_ = stringToDouble() != 0 ? true : false;
|
|
}
|
|
if (segment_->type_ != Segment::BOOL)
|
|
{
|
|
if (!context_ || !context_->data_ || len_ < 1)
|
|
{
|
|
Log("JsonNode is no BOOL NUMBER");
|
|
}
|
|
else
|
|
{
|
|
segment_->setType(Segment::BOOL);
|
|
segment_->value_.bool_ = stringToDouble() != 0 ? true : false;
|
|
}
|
|
}
|
|
switch (segment_->type_)
|
|
{
|
|
case OpenJson::Segment::BOOL:
|
|
return segment_->value_.bool_;
|
|
case OpenJson::Segment::INT32:
|
|
return (bool)segment_->value_.int32_;
|
|
case OpenJson::Segment::INT64:
|
|
return (bool)segment_->value_.int64_;
|
|
case OpenJson::Segment::DOUBLE:
|
|
return (bool)segment_->value_.double_;
|
|
case OpenJson::Segment::STRING:
|
|
return segment_->content_.size() > 0;
|
|
default:
|
|
break;
|
|
}
|
|
return def;
|
|
}
|
|
|
|
int32_t OpenJson::i32(int32_t def)
|
|
{
|
|
if (type_ != NUMBER)
|
|
{
|
|
Log("JsonNode is no NUMBER");
|
|
return def;
|
|
}
|
|
if (segment_ == 0)
|
|
{
|
|
if (!context_ || !context_->data_ || len_ < 1)
|
|
{
|
|
return def;
|
|
}
|
|
segment_ = new Segment(Segment::INT32);
|
|
segment_->value_.int32_ = stringToInt32();
|
|
}
|
|
if (segment_->type_ != Segment::INT32)
|
|
{
|
|
if (!context_ || !context_->data_ || len_ < 1)
|
|
{
|
|
Log("JsonNode is no INT32 NUMBER");
|
|
}
|
|
else
|
|
{
|
|
segment_->setType(Segment::INT32);
|
|
segment_->value_.int32_ = stringToInt32();
|
|
}
|
|
}
|
|
switch (segment_->type_)
|
|
{
|
|
case OpenJson::Segment::BOOL:
|
|
return segment_->value_.bool_;
|
|
case OpenJson::Segment::INT32:
|
|
return segment_->value_.int32_;
|
|
case OpenJson::Segment::INT64:
|
|
return (int32_t)segment_->value_.int64_;
|
|
case OpenJson::Segment::DOUBLE:
|
|
return (int32_t)segment_->value_.double_;
|
|
case OpenJson::Segment::STRING:
|
|
return atoi(segment_->content_.c_str());
|
|
default:
|
|
break;
|
|
}
|
|
return def;
|
|
}
|
|
|
|
int64_t OpenJson::i64(int64_t def)
|
|
{
|
|
if (type_ != NUMBER)
|
|
{
|
|
Log("JsonNode is no NUMBER");
|
|
return def;
|
|
}
|
|
if (segment_ && segment_->type_ == Segment::NIL)
|
|
{
|
|
delete segment_;
|
|
segment_ = 0;
|
|
}
|
|
if (segment_ == 0)
|
|
{
|
|
if (!context_ || !context_->data_ || len_ < 1)
|
|
{
|
|
return def;
|
|
}
|
|
segment_ = new Segment(Segment::INT64);
|
|
segment_->value_.int64_ = stringToInt64();
|
|
}
|
|
if (segment_->type_ != Segment::INT64)
|
|
{
|
|
Log("JsonNode is no INT64 NUMBER");
|
|
}
|
|
switch (segment_->type_)
|
|
{
|
|
case OpenJson::Segment::BOOL:
|
|
return segment_->value_.bool_;
|
|
case OpenJson::Segment::INT32:
|
|
return segment_->value_.int32_;
|
|
case OpenJson::Segment::INT64:
|
|
return segment_->value_.int64_;
|
|
case OpenJson::Segment::DOUBLE:
|
|
return (int64_t)segment_->value_.double_;
|
|
case OpenJson::Segment::STRING:
|
|
return atoll(segment_->content_.c_str());
|
|
default:
|
|
break;
|
|
}
|
|
return def;
|
|
}
|
|
|
|
void OpenJson::operator=(const std::string& val)
|
|
{
|
|
if (type_ == OBJECT || type_ == ARRAY)
|
|
{
|
|
Log("JsonNode is a container, not element");
|
|
return;
|
|
}
|
|
if (type_ != STRING) type_ = STRING;
|
|
if (segment_ == 0) segment_ = new Segment;
|
|
segment_->setType(Segment::STRING);
|
|
//const char* ptr = 0;
|
|
for (size_t i = 0; i < val.size(); ++i)
|
|
{
|
|
if (val[i] == '"' || val[i] == '\'')
|
|
{
|
|
segment_->content_.push_back('\\');
|
|
}
|
|
segment_->content_.push_back(val[i]);
|
|
}
|
|
}
|
|
|
|
void OpenJson::operator=(const char* val)
|
|
{
|
|
if (type_ == OBJECT || type_ == ARRAY)
|
|
{
|
|
Log("JsonNode is a container, not element");
|
|
return;
|
|
}
|
|
if (type_ != STRING) type_ = STRING;
|
|
if (segment_ == 0) segment_ = new Segment;
|
|
segment_->setType(Segment::STRING);
|
|
segment_->content_.clear();
|
|
const char* ptr = 0;
|
|
for (size_t i = 0; i < strlen(val); ++i)
|
|
{
|
|
ptr = val + i;
|
|
if (*ptr == '"' || *ptr == '\'')
|
|
{
|
|
segment_->content_.push_back('\\');
|
|
}
|
|
segment_->content_.push_back(*ptr);
|
|
}
|
|
}
|
|
|
|
void OpenJson::operator=(bool val)
|
|
{
|
|
if (type_ == OBJECT || type_ == ARRAY)
|
|
{
|
|
Log("JsonNode is a container, not element");
|
|
return;
|
|
}
|
|
if (type_ != NUMBER) type_ = NUMBER;
|
|
if (segment_ == 0) segment_ = new Segment;
|
|
segment_->setType(Segment::BOOL);
|
|
segment_->value_.bool_ = val;
|
|
}
|
|
|
|
void OpenJson::operator=(int32_t val)
|
|
{
|
|
if (type_ == OBJECT || type_ == ARRAY)
|
|
{
|
|
Log("JsonNode is a container, not element");
|
|
return;
|
|
}
|
|
if (type_ != NUMBER) type_ = NUMBER;
|
|
if (segment_ == 0) segment_ = new Segment;
|
|
segment_->setType(Segment::INT32);
|
|
segment_->value_.int32_ = val;
|
|
}
|
|
|
|
void OpenJson::operator=(uint32_t val)
|
|
{
|
|
if (type_ == OBJECT || type_ == ARRAY)
|
|
{
|
|
Log("JsonNode is a container, not element");
|
|
return;
|
|
}
|
|
if (type_ != NUMBER) type_ = NUMBER;
|
|
if (segment_ == 0) segment_ = new Segment;
|
|
segment_->setType(Segment::INT32);
|
|
segment_->value_.int32_ = val;
|
|
}
|
|
|
|
void OpenJson::operator=(int64_t val)
|
|
{
|
|
if (type_ == OBJECT || type_ == ARRAY)
|
|
{
|
|
Log("JsonNode is a container, not element");
|
|
return;
|
|
}
|
|
if (type_ != NUMBER) type_ = NUMBER;
|
|
if (segment_ == 0) segment_ = new Segment;
|
|
segment_->setType(Segment::INT64);
|
|
segment_->value_.int64_ = val;
|
|
}
|
|
|
|
void OpenJson::operator=(uint64_t val)
|
|
{
|
|
if (type_ == OBJECT || type_ == ARRAY)
|
|
{
|
|
Log("JsonNode is a container, not element");
|
|
return;
|
|
}
|
|
if (type_ != NUMBER) type_ = NUMBER;
|
|
if (segment_ == 0) segment_ = new Segment;
|
|
segment_->setType(Segment::INT64);
|
|
segment_->value_.int64_ = val;
|
|
}
|
|
|
|
void OpenJson::operator=(double val)
|
|
{
|
|
if (type_ == OBJECT || type_ == ARRAY)
|
|
{
|
|
Log("JsonNode is a container");
|
|
return;
|
|
}
|
|
if (type_ != NUMBER) type_ = NUMBER;
|
|
if (segment_ == 0) segment_ = new Segment;
|
|
segment_->setType(Segment::DOUBLE);
|
|
segment_->value_.double_ = val;
|
|
}
|
|
|
|
OpenJson& OpenJson::array(size_t idx)
|
|
{
|
|
if (type_ != ARRAY)
|
|
{
|
|
if (type_ == OBJECT)
|
|
{
|
|
Log("JsonNode must be ARRAY, not OBJECT");
|
|
}
|
|
type_ = ARRAY;
|
|
}
|
|
else
|
|
{
|
|
assert(box_);
|
|
}
|
|
if (!box_) box_ = new Box;
|
|
if (idx >= box_->childs_.size())
|
|
{
|
|
box_->childs_.resize(idx + 1, 0);
|
|
}
|
|
OpenJson* child = box_->childs_[idx];
|
|
if (!child)
|
|
{
|
|
child = new OpenJson();
|
|
box_->childs_[idx] = child;
|
|
}
|
|
return *child;
|
|
}
|
|
|
|
OpenJson& OpenJson::object(const char* key)
|
|
{
|
|
if (!key)
|
|
{
|
|
return NodeNull;
|
|
}
|
|
if (type_ != OBJECT)
|
|
{
|
|
if (type_ == ARRAY)
|
|
{
|
|
Log("JsonNode must be OBJECT, not ARRAY");
|
|
}
|
|
type_ = OBJECT;
|
|
}
|
|
else
|
|
{
|
|
assert(box_);
|
|
}
|
|
if (!box_) box_ = new Box;
|
|
|
|
OpenJson* child = 0;
|
|
for (size_t i = 0; i < box_->childs_.size(); ++i)
|
|
{
|
|
child = box_->childs_[i];
|
|
if (child == 0) continue;
|
|
if (strcmp(child->key().c_str(), key) == 0)
|
|
{
|
|
return *child;
|
|
}
|
|
}
|
|
OpenJson* keyNode = new OpenJson(STRING);
|
|
*keyNode = key;
|
|
child = new OpenJson();
|
|
child->key_ = keyNode;
|
|
size_t i = 0;
|
|
for (; i < box_->childs_.size(); ++i)
|
|
{
|
|
if (!box_->childs_[i])
|
|
{
|
|
box_->childs_[i] = child;
|
|
break;
|
|
}
|
|
}
|
|
if (i >= box_->childs_.size())
|
|
{
|
|
box_->childs_.push_back(child);
|
|
}
|
|
return *child;
|
|
}
|
|
|
|
void OpenJson::addNode(OpenJson* node)
|
|
{
|
|
if (!node) return;
|
|
if (type_ != OBJECT && type_ != ARRAY)
|
|
{
|
|
Log("JsonNode must be OBJECT or ARRAY");
|
|
type_ = node->key_ ? OBJECT : ARRAY;
|
|
}
|
|
if (box_ == 0) box_ = new Box;
|
|
box_->add(node);
|
|
}
|
|
|
|
void OpenJson::removeNode(size_t idx)
|
|
{
|
|
if (box_ == 0) return;
|
|
if (idx >= box_->childs_.size()) return;
|
|
box_->remove(box_->childs_[idx]);
|
|
}
|
|
|
|
void OpenJson::removeNode(const char* key)
|
|
{
|
|
if (box_ == 0) return;
|
|
OpenJson* child = 0;
|
|
for (size_t i = 0; i < box_->childs_.size(); ++i)
|
|
{
|
|
child = box_->childs_[i];
|
|
if (child == 0) continue;
|
|
if (strcmp(child->key().c_str(), key) == 0)
|
|
{
|
|
box_->remove(child);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void OpenJson::clear()
|
|
{
|
|
if (segment_)
|
|
{
|
|
delete segment_;
|
|
segment_ = 0;
|
|
}
|
|
if (key_)
|
|
{
|
|
delete key_;
|
|
key_ = 0;
|
|
}
|
|
if (box_)
|
|
{
|
|
assert(type_ == OBJECT || type_ == ARRAY);
|
|
delete box_;
|
|
box_ = 0;
|
|
}
|
|
if (context_ != 0 && context_->root_ == this)
|
|
{
|
|
context_->root_ = 0;
|
|
delete context_;
|
|
}
|
|
context_ = 0;
|
|
if (wcontext_ != 0 && wcontext_->root_ == this)
|
|
{
|
|
wcontext_->root_ = 0;
|
|
delete wcontext_;
|
|
}
|
|
wcontext_ = 0;
|
|
type_ = EMPTY;
|
|
idx_ = 0;
|
|
len_ = 0;
|
|
}
|
|
|
|
void OpenJson::trimSpace()
|
|
{
|
|
if (!context_) return;
|
|
char code = 0;
|
|
for (size_t i = idx_; i < context_->size_; ++i)
|
|
{
|
|
code = context_->data_[i];
|
|
if (code > ' ')
|
|
{
|
|
idx_ = i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
unsigned char OpenJson::getCharCode()
|
|
{
|
|
if (!context_) return 0;
|
|
if (idx_ < context_->size_)
|
|
{
|
|
unsigned char tmp = (unsigned char)context_->data_[idx_];
|
|
return tmp;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
unsigned char OpenJson::getChar()
|
|
{
|
|
unsigned char code = getCharCode();
|
|
if (code <= ' ')
|
|
{
|
|
trimSpace();
|
|
code = getCharCode();
|
|
}
|
|
return code;
|
|
}
|
|
|
|
unsigned char OpenJson::checkCode(unsigned char charCode)
|
|
{
|
|
unsigned char code = getCharCode();
|
|
if (code != charCode)
|
|
{
|
|
trimSpace();
|
|
code = getCharCode();
|
|
if (code != charCode) return 0;
|
|
}
|
|
++idx_;
|
|
return code;
|
|
}
|
|
|
|
size_t OpenJson::searchCode(unsigned char code)
|
|
{
|
|
char* data = context_->data_;
|
|
for (size_t i = idx_; i < context_->size_; i++)
|
|
{
|
|
if (data[i] == code)
|
|
{
|
|
if (i > 0 && data[i - 1] != '\\') return i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
bool OpenJson::makeRContext()
|
|
{
|
|
if (type_ != EMPTY)
|
|
{
|
|
if (context_ && context_->root_ != this)
|
|
{
|
|
PRINTF("OpenJson warn:JsonNode is no root or empty!");
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (context_ && context_->root_ != this)
|
|
{
|
|
PRINTF("OpenJson warn:JsonNode is no root or empty!");
|
|
return false;
|
|
};
|
|
}
|
|
clear();
|
|
context_ = new Context();
|
|
context_->root_ = this;
|
|
context_->offset_ = 0;
|
|
context_->rbuffer_.clear();
|
|
return true;
|
|
}
|
|
|
|
bool OpenJson::decode(const std::string& buffer)
|
|
{
|
|
if (!makeRContext()) return false;
|
|
context_->rbuffer_ = buffer;
|
|
context_->startRead();
|
|
type_ = codeToType(getChar());
|
|
try
|
|
{
|
|
read(context_, true);
|
|
}
|
|
catch (const char* error)
|
|
{
|
|
PRINTF("OpenJson warn:decode catch exception %s", error);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool OpenJson::decodeFile(const std::string& filePath)
|
|
{
|
|
if (!makeRContext()) return false;
|
|
FILE* fp = 0;
|
|
#ifdef _MSC_VER
|
|
fopen_s(&fp, filePath.c_str(), "rb");
|
|
#else
|
|
fp = fopen(filePath.c_str(), "rb");
|
|
#endif
|
|
if (fp == 0)
|
|
{
|
|
#ifdef _MSC_VER
|
|
char buffer[1024] = { 0 };
|
|
strerror_s(buffer, sizeof(buffer), errno);
|
|
PRINTF("OpenJson warn:decodeFile error:%s,%s\n", buffer, filePath.c_str());
|
|
#else
|
|
PRINTF("OpenJson warn:decodeFile error:%s,%s\n", strerror(errno), filePath.c_str());
|
|
#endif
|
|
return false;
|
|
}
|
|
fseek(fp, 0, SEEK_END);
|
|
/*size_t size = */
|
|
ftell(fp);
|
|
fseek(fp, 0, SEEK_SET);
|
|
|
|
size_t ret = 0;
|
|
char buff[1024 * 8] = { 0 };
|
|
while (true)
|
|
{
|
|
ret = fread(buff, 1, sizeof(buff), fp);
|
|
if (ret < 0)
|
|
{
|
|
#ifdef _MSC_VER
|
|
char buffer[1024] = { 0 };
|
|
strerror_s(buffer, sizeof(buffer), errno);
|
|
PRINTF("OpenJson warn:decodeFile error:%s,%s\n", buffer, filePath.c_str());
|
|
#else
|
|
PRINTF("OpenJson warn:decodeFile error:%s,%s\n", strerror(errno), filePath.c_str());
|
|
#endif
|
|
fclose(fp);
|
|
return false;
|
|
}
|
|
else if(ret == 0) break;
|
|
context_->rbuffer_.append(buff, ret);
|
|
}
|
|
fclose(fp);
|
|
|
|
context_->startRead();
|
|
type_ = codeToType(getChar());
|
|
try
|
|
{
|
|
read(context_, true);
|
|
}
|
|
catch (const char* error)
|
|
{
|
|
PRINTF("OpenJson warn:decodeFile catch exception %s", error);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
const std::string& OpenJson::encode()
|
|
{
|
|
if (wcontext_ == 0)
|
|
{
|
|
wcontext_ = new Context();
|
|
wcontext_->root_ = this;
|
|
}
|
|
wcontext_->startWrite();
|
|
write(wcontext_, true);
|
|
return wcontext_->wbuffer_;
|
|
}
|
|
|
|
void OpenJson::encodeFile(const std::string& filePath)
|
|
{
|
|
FILE* fp = 0;
|
|
#ifdef _MSC_VER
|
|
fopen_s(&fp, filePath.c_str(), "wb");
|
|
#else
|
|
fp = fopen(filePath.c_str(), "wb");
|
|
#endif
|
|
if (fp == 0)
|
|
{
|
|
#ifdef _MSC_VER
|
|
char buffer[1024] = { 0 };
|
|
strerror_s(buffer, sizeof(buffer), errno);
|
|
PRINTF("OpenJson warn:encodeFile error:%s,%s\n", buffer, filePath.c_str());
|
|
#else
|
|
PRINTF("OpenJson warn:encodeFile error:%s,%s\n", strerror(errno), filePath.c_str());
|
|
#endif
|
|
return;
|
|
}
|
|
fseek(fp, 0, SEEK_SET);
|
|
const std::string& buffer = encode();
|
|
fwrite(buffer.data(), buffer.size(), 1, fp);
|
|
fclose(fp);
|
|
}
|
|
|
|
void OpenJson::read(Context* context, bool isRoot)
|
|
{
|
|
if (context_)
|
|
{
|
|
if (isRoot)
|
|
{
|
|
assert(context_ == context);
|
|
assert(context_->root_ == this);
|
|
}
|
|
else
|
|
{
|
|
assert(context_->root_ != this);
|
|
if (context_->root_ == this) return;
|
|
}
|
|
}
|
|
len_ = 0;
|
|
context_ = context;
|
|
idx_ = context->offset_;
|
|
switch (type_)
|
|
{
|
|
case EMPTY:
|
|
break;
|
|
case STRING:
|
|
readString();
|
|
break;
|
|
case NUMBER:
|
|
readNumber();
|
|
break;
|
|
case OBJECT:
|
|
readObject();
|
|
break;
|
|
case ARRAY:
|
|
readArray();
|
|
break;
|
|
case UNKNOWN:
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
void OpenJson::readNumber()
|
|
{
|
|
assert(type_ == NUMBER);
|
|
unsigned char code = 0;
|
|
size_t sidx = idx_;
|
|
size_t len = context_->size_;
|
|
char* data = context_->data_;
|
|
for (; idx_ < len; idx_++)
|
|
{
|
|
code = data[idx_];
|
|
if (code == ',' || code == '}' || code == ']')
|
|
{
|
|
idx_--;
|
|
break;
|
|
}
|
|
}
|
|
if (idx_ < sidx)
|
|
{
|
|
throwError("lost number value");
|
|
return;
|
|
}
|
|
len_ = idx_ - sidx + 1;
|
|
idx_ = sidx;
|
|
}
|
|
void OpenJson::readString()
|
|
{
|
|
assert(type_ == STRING);
|
|
unsigned char code = '"';
|
|
if (!checkCode(code))
|
|
{
|
|
code = '\'';
|
|
if (!checkCode(code))
|
|
{
|
|
throwError("lost '\"' or \"'\"");
|
|
return;
|
|
}
|
|
}
|
|
size_t sidx = idx_;
|
|
size_t eidx = searchCode(code);
|
|
if (eidx < 0)
|
|
{
|
|
throwError("lost '\"' or \"'\"");
|
|
return;
|
|
}
|
|
idx_ = sidx;
|
|
len_ = eidx - sidx + 1;
|
|
context_->data_[eidx] = 0;
|
|
}
|
|
void OpenJson::readObject()
|
|
{
|
|
assert(type_ == OBJECT);
|
|
if (!checkCode('{'))
|
|
{
|
|
throwError("lost '{'");
|
|
return;
|
|
}
|
|
unsigned char code = 0;
|
|
OpenJson* keyNode = 0;
|
|
OpenJson* valNode = 0;
|
|
size_t oidx = idx_;
|
|
while (idx_ < context_->size_)
|
|
{
|
|
code = getChar();
|
|
if (code == 0)
|
|
{
|
|
throwError("lost '}'");
|
|
return;
|
|
}
|
|
if (checkCode('}')) break;
|
|
keyNode = createNode(code);
|
|
if (keyNode->type_ != STRING)
|
|
{
|
|
throwError("lost key");
|
|
return;
|
|
}
|
|
context_->offset_ = idx_;
|
|
keyNode->read(context_);
|
|
idx_ = keyNode->idx_ + keyNode->len_;
|
|
if (!checkCode(':'))
|
|
{
|
|
throwError("lost ':'");
|
|
return;
|
|
}
|
|
code = getChar();
|
|
valNode = createNode(code);
|
|
valNode->key_ = keyNode;
|
|
context_->offset_ = idx_;
|
|
valNode->read(context_);
|
|
idx_ = valNode->idx_ + valNode->len_;
|
|
addNode(valNode);
|
|
|
|
if (checkCode('}'))
|
|
{
|
|
context_->data_[idx_ - 1] = 0;
|
|
break;
|
|
}
|
|
if (!checkCode(','))
|
|
{
|
|
throwError("lost ','");
|
|
return;
|
|
}
|
|
context_->data_[idx_ - 1] = 0;
|
|
}
|
|
len_ = idx_ - oidx;
|
|
idx_ = oidx;
|
|
}
|
|
void OpenJson::readArray()
|
|
{
|
|
assert(type_ == ARRAY);
|
|
if (!checkCode('['))
|
|
{
|
|
throwError("lost '['");
|
|
return;
|
|
}
|
|
unsigned char code = 0;
|
|
OpenJson* valNode = 0;
|
|
size_t oidx = idx_;
|
|
while (idx_ < context_->size_)
|
|
{
|
|
code = getChar();
|
|
if (code == 0)
|
|
{
|
|
throwError("lost ']'");
|
|
return;
|
|
}
|
|
if (checkCode(']')) break;
|
|
valNode = createNode(code);
|
|
context_->offset_ = idx_;
|
|
valNode->read(context_);
|
|
idx_ = valNode->idx_ + valNode->len_;
|
|
addNode(valNode);
|
|
|
|
if (checkCode(']'))
|
|
{
|
|
context_->data_[idx_ - 1] = 0;
|
|
break;
|
|
}
|
|
if (!checkCode(','))
|
|
{
|
|
throwError("lost ','");
|
|
return;
|
|
}
|
|
context_->data_[idx_ - 1] = 0;
|
|
}
|
|
len_ = idx_ - oidx;
|
|
idx_ = oidx;
|
|
}
|
|
|
|
void OpenJson::write(Context* context, bool isRoot)
|
|
{
|
|
if (wcontext_)
|
|
{
|
|
if (isRoot)
|
|
{
|
|
assert(wcontext_ == context);
|
|
assert(wcontext_->root_ == this);
|
|
}
|
|
else
|
|
{
|
|
assert(wcontext_->root_ != this);
|
|
if (wcontext_->root_ == this) return;
|
|
}
|
|
}
|
|
wcontext_ = context;
|
|
switch (type_)
|
|
{
|
|
case EMPTY:
|
|
break;
|
|
case STRING:
|
|
writeString();
|
|
break;
|
|
case NUMBER:
|
|
writeNumber();
|
|
break;
|
|
case OBJECT:
|
|
writeObject();
|
|
break;
|
|
case ARRAY:
|
|
writeArray();
|
|
break;
|
|
case UNKNOWN:
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
void OpenJson::writeNumber()
|
|
{
|
|
assert(type_ == NUMBER);
|
|
if (key_)
|
|
{
|
|
wcontext_->wbuffer_.append("\"" + key() + "\":");
|
|
}
|
|
if (segment_)
|
|
{
|
|
segment_->toString();
|
|
wcontext_->wbuffer_.append(segment_->content_);
|
|
}
|
|
else
|
|
{
|
|
wcontext_->wbuffer_.append(data());
|
|
}
|
|
}
|
|
void OpenJson::writeString()
|
|
{
|
|
assert(type_ == STRING);
|
|
if (key_)
|
|
{
|
|
wcontext_->wbuffer_.append("\"" + key() + "\":");
|
|
}
|
|
wcontext_->wbuffer_.append("\"" + s() + "\"");
|
|
}
|
|
void OpenJson::writeObject()
|
|
{
|
|
assert(type_ == OBJECT);
|
|
if (key_)
|
|
wcontext_->wbuffer_.append("\"" + key() + "\":{");
|
|
else
|
|
wcontext_->wbuffer_.append("{");
|
|
|
|
if (box_ != 0)
|
|
{
|
|
size_t idx = 0;
|
|
size_t size = box_->size();
|
|
for (size_t i = 0; i < size; ++i)
|
|
{
|
|
if (!(*box_)[i]) continue;
|
|
if (idx > 0)
|
|
{
|
|
wcontext_->wbuffer_.append(",");
|
|
}
|
|
(*box_)[i]->write(wcontext_);
|
|
++idx;
|
|
}
|
|
}
|
|
wcontext_->wbuffer_.append("}");
|
|
}
|
|
void OpenJson::writeArray()
|
|
{
|
|
assert(type_ == ARRAY);
|
|
if (key_)
|
|
wcontext_->wbuffer_.append("\"" + key() + "\":[");
|
|
else
|
|
wcontext_->wbuffer_.append("[");
|
|
|
|
if (box_ != 0)
|
|
{
|
|
size_t idx = 0;
|
|
size_t size = box_->size();
|
|
for (size_t i = 0; i < size; ++i)
|
|
{
|
|
if (!(*box_)[i]) continue;
|
|
if (idx > 0)
|
|
{
|
|
wcontext_->wbuffer_.append(",");
|
|
}
|
|
(*box_)[i]->write(wcontext_);
|
|
++idx;
|
|
}
|
|
}
|
|
wcontext_->wbuffer_.append("]");
|
|
}
|
|
|
|
void OpenJson::EnableLog(bool enable)
|
|
{
|
|
EnableLog_ = enable;
|
|
}
|
|
|
|
void OpenJson::Log(const char* format, ...)
|
|
{
|
|
if (!EnableLog_) return;
|
|
va_list ap;
|
|
va_start(ap, format);
|
|
char tmp[1024] = { 0 };
|
|
vsnprintf(tmp, sizeof(tmp), format, ap);
|
|
va_end(ap);
|
|
PRINTF("OpenJson WARN:%s\n", tmp);
|
|
}
|
|
void OpenJson::throwError(const char* errMsg)
|
|
{
|
|
static const char* InfoTags[6] = { "EMPTY", "STRING", "NUMBER", "OBJECT", "ARRAY", "UNKNOWN" };
|
|
size_t len = sizeof(InfoTags) / sizeof(InfoTags[0]);
|
|
const char* tab = type_ < len ? InfoTags[type_] : InfoTags[5];
|
|
PRINTF("OpenJson:throwError [%s] Error: %s\n", tab, errMsg);
|
|
|
|
char tmp[126] = { 0 };
|
|
len = context_->size_ - context_->offset_;
|
|
len = len > 64 ? 64 : len;
|
|
memcpy(tmp, context_->data_ + idx_, len);
|
|
PRINTF("OpenJson:throwError content:%s\n", tmp);
|
|
throw errMsg;
|
|
}
|
|
|