OpenJson是世界上最易用的高性能C++json解析器
2023-03-07 09:09:31 时间
原文及源代码:https://mp.weixin.qq.com/s/doD_JNm3rNBqQIOrscOnqw
OpenJson是世界上最好用的高性能C++json解析器,非常简单易用,解析速度超快,可以解析超过1GB以上的json文件。
测试例子
#include "openjson.h"
#include <iostream>
#include <assert.h>
int main()
{
std::string buff1;
//生成json字符串
{
OpenJson json;
auto& nodeA = json["a"];
nodeA["aa"] = "aa12";
nodeA["ab"] = 123;
auto& nodeB = json["b"];
//迭代数组原生
for (size_t i = 0; i < 2; i++)
{
auto& node = nodeB[i];
if (i == 0)
{
node["ba"]["key1"] = std::string("value_ba");
}
else
{
uint64_t val = 999999999999999;
node["bb"][0] = val;
node["bb"][1] = 1.3;
}
}
//设置值
json["b"][1]["bb"][2] = true;
buff1 = json.encode();
}
std::string buff2 = "{"
"\"a\":{"
"\"aa\":\"aa12\","
"\"ab\":123"
"},"
"\"b\":["
"{\"ba\":{\"key1\":\"value_ba\"}},"
"{\"bb\":[999999999999999,1.3,true]}"
"]}";
assert(buff1 == buff2);
//解析json字符串
{
OpenJson json;
json.decode(buff2);
auto& nodeA = json["a"];
assert(nodeA["aa"].s() == "aa12");
assert(nodeA["ab"].i32() == 123);
auto& nodeB = json["b"];
for (size_t i = 0; i < nodeB.size(); i++)
{
auto& node = nodeB[i];
if (i == 0)
{
assert(node["ba"]["key1"].s() == "value_ba");
}
else
{
assert(node["bb"][0].i64() == 999999999999999);
assert(node["bb"][1].d() == 1.3);
}
}
assert(json["b"][1]["bb"][2].b() == true);
//删除元素
json["b"][1]["bb"].remove(1);
json.encodeFile("./test.json");
}
buff2 = "{"
"\"a\":{"
"\"aa\":\"aa12\","
"\"ab\":123"
"},"
"\"b\":["
"{\"ba\":{\"key1\":\"value_ba\"}},"
"{\"bb\":[999999999999999,true]}"
"]}";
{
//加载json文件
OpenJson json;
json.decodeFile("./test.json");
buff1 = json.encode();
assert(buff1 == buff2);
}
buff2 = "{\"b\":[{\"bb\":[99999]}]}";
{
OpenJson json;
json["b"][1]["bb"][2] = 99999;
buff1 = json.encode();
assert(buff1 == buff2);
}
std::cout << "公众号:https://mp.weixin.qq.com/s/doD_JNm3rNBqQIOrscOnqw" << std::endl;
return 0;
}
运行环境
Windows、linux等跨平台设计
编译和运行
cd ./openjson
mkdir build
cd build
cmake ..
make
./test
全部源文件
. src/openjson.h
/***************************************************************************
* 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.
***************************************************************************/
#ifndef HEADER_OPEN_JSON_H
#define HEADER_OPEN_JSON_H
#include <string>
#include <stddef.h>
#include <stdint.h>
#include <vector>
#ifdef _MSC_VER
#if _MSC_VER >= 1600 || defined(__MINGW32__)
#else
#if (_MSC_VER < 1300)
typedef signed char int8_t;
typedef signed short int16_t;
typedef signed int int32_t;
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;
#else
typedef signed __int8 int8_t;
typedef signed __int16 int16_t;
typedef signed __int32 int32_t;
typedef unsigned __int8 uint8_t;
typedef unsigned __int16 uint16_t;
typedef unsigned __int32 uint32_t;
#endif
typedef signed __int64 int64_t;
typedef unsigned __int64 uint64_t;
#endif
#endif // _MSC_VER
class OpenJson
{
enum JsonType
{
EMPTY = 0,
STRING,
NUMBER,
OBJECT,
ARRAY,
UNKNOWN
};
class Box
{
std::vector<OpenJson*> childs_;
public:
Box();
~Box();
inline void clear() { childs_.clear(); }
inline bool empty() { return childs_.empty(); }
inline size_t size() { return childs_.size(); }
inline void add(OpenJson* node){ childs_.push_back(node); }
inline OpenJson* operator[](size_t idx){ return childs_[idx]; }
bool remove(OpenJson* node);
friend class OpenJson;
};
class Context
{
char* data_;
size_t size_;
size_t offset_;
OpenJson* root_;
std::string rbuffer_;
std::string wbuffer_;
std::string stringNull_;
public:
Context();
~Context();
void startRead();
void startWrite();
friend class OpenJson;
};
class Segment
{
public:
enum SegmentType
{
NIL = 0,
BOOL,
INT32,
INT64,
DOUBLE,
STRING
};
SegmentType type_;
std::string content_;
union {
bool bool_;
int32_t int32_;
int64_t int64_;
double double_;
} value_;
Segment(SegmentType type = NIL);
~Segment();
void clear();
void toString();
void setType(SegmentType type);
};
JsonType type_;
Context* context_;
Context* wcontext_;
size_t idx_;
size_t len_;
Box* box_;
OpenJson* key_;
Segment* segment_;
void trimSpace();
bool makeRContext();
OpenJson* createNode(unsigned char code);
JsonType codeToType(unsigned char code);
unsigned char getCharCode();
unsigned char getChar();
unsigned char checkCode(unsigned char charCode);
size_t searchCode(unsigned char charCode);
void throwError(const char* errMsg);
const char* data();
int32_t stringToInt32();
int64_t stringToInt64();
double stringToDouble();
OpenJson& array(size_t idx);
OpenJson& object(const char* key);
void addNode(OpenJson* node);
void removeNode(size_t idx);
void removeNode(const char* key);
OpenJson(OpenJson& json) {}
OpenJson(const OpenJson& json) {}
void operator=(OpenJson& json) {}
void operator=(const OpenJson& json) {}
const std::string& emptyString();
static OpenJson NodeNull;
static std::string StringNull;
public:
OpenJson(JsonType type = EMPTY);
~OpenJson();
inline size_t size() { return box_ ? box_->size() : 0; }
inline bool empty() { return box_ ? box_->empty() : true; }
inline OpenJson& operator[] (int idx) { return array(idx); }
inline OpenJson& operator[] (size_t idx) { return array(idx); }
inline OpenJson& operator[] (const char* key) { return object(key); }
inline OpenJson& operator[] (const std::string& key) { return object(key.c_str()); }
inline void remove(int idx) { removeNode(idx); }
inline void remove(size_t idx) { removeNode(idx); }
inline void remove(const char* key) { removeNode(key); }
inline void remove(const std::string& key) { removeNode(key.c_str()); }
void clear();
inline bool isNull() { return type_ == EMPTY; }
inline bool isNumber() { return type_ == NUMBER; }
inline bool isString() { return type_ == STRING; }
inline bool isObject() { return type_ == OBJECT; }
inline bool isArray() { return type_ == ARRAY; }
bool b(bool def = false);
int32_t i32(int32_t def = 0);
int64_t i64(int64_t def = 0);
double d(double def = 0);
const std::string& s();
const std::string& key();
void operator=(bool val);
void operator=(int32_t val);
void operator=(uint32_t val);
void operator=(int64_t val);
void operator=(uint64_t val);
void operator=(double val);
void operator=(const char* val);
void operator=(const std::string& val);
bool decode(const std::string& buffer);
bool decodeFile(const std::string& filePath);
const std::string& encode();
void encodeFile(const std::string& filePath);
static void EnableLog(bool enable);
private:
static bool EnableLog_;
static void Log(const char* format, ...);
void read(Context* context, bool isRoot = false);
void readNumber();
void readString();
void readObject();
void readArray();
void write(Context* context, bool isRoot = false);
void writeNumber();
void writeString();
void writeObject();
void writeArray();
};
#endif /* HEADER_OPEN_JSON_H */
. src/openjson.cpp
/***************************************************************************
* 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 "openjson.h"
#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>
#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()
:root_(0),
offset_(0),
data_(0),
size_(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),
key_(0),
box_(0),
segment_(0),
idx_(0),
len_(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);
segment_->content_ = val;
}
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_ = val;
}
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::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;
}
相关文章
- 在 Go 里用 CGO?这 7 个问题你要关注!
- 9款优秀的去中心化通讯软件 Matrix 的客户端
- 求职数据分析,项目经验该怎么写
- 在OKR中,我看到了数据驱动业务的未来
- 火山引擎云原生大数据在金融行业的实践
- OpenHarmony富设备移植指南(二)—从postmarketOS获取移植资源
- 《数据成熟度指数》报告:64%的企业领袖认为大多数员工“不懂数据”
- OpenHarmony 小型系统兼容性测试指南
- 肯睿中国(Cloudera):2023年企业数字战略三大趋势预测
- 适用于 Linux 的十大命令行游戏
- GNOME 截图工具的新旧截图方式
- System76 即将推出的 COSMIC 桌面正在酝酿大变化
- 2GB 内存 8GB 存储即可流畅运行,Windows 11 极致精简版系统 Tiny11 发布
- 迎接 ecode:一个即将推出的具有全新图形用户界面框架的现代、轻量级代码编辑器
- loongarch架构介绍(三)—地址翻译
- Go 语言怎么解决编译器错误“err is shadowed during return”?
- 敏捷:可能被开发人员遗忘的部分
- Denodo预测2023年数据管理和分析的未来
- 利用数据推动可持续发展
- 在 Vue3 中实现 React 原生 Hooks(useState、useEffect),深入理解 React Hooks 的