C++轻量级Json解析工具—TinyJson
简介
之前Json解析工具习惯于用**nlohmann/json**,后来发现要兼容Centos5系的系统,且需要批量使用,系统升级gcc/g++升级有点不现实,后来改用轻量级TinyJson库,说一下在使用碰到的一些问题以及为了兼容性作出的修改。
TinyJson轻量级json解析工具,只需要包含头就可以直接使用。兼容Windows/Linux平台。Mac并没有进行相关测试。理论上兼容。
TinyJson相关信息
Git地址:https://github.com/button-chen/tinyjson
相关介绍,在github并没有过的介绍,但是在百度的时候发现了很多关于TinyJson的介绍,有点乱,不一一细究了。
- 使用样例
在github中有相关的一些介绍,不做详细介绍。
测试缺陷
1.兼容C++03 编译
Lamba表达式的去除
char LastValidChar(std::string json,int index)
{
for (int i = index - 1; i >= 0; --i) {
if (isspace(json[i])) continue;
char tmp = json[i];
return tmp;
}
return '\0';
}
//-------------------------------------------------------------//
//将下方的lamba表达式注释掉,并改为添加上的函数//
// 解析为 key-value 调用一次解析一个层次
inline bool ParseJson::ParseObj(std::string json) {
//------注释区域--------------//
auto LastValidChar = [&](int index)->char{
for (int i = index-1; i >= 0; --i){
if (isspace(json[i])) continue;
char tmp = json[i];
return tmp;
}
return '\0';
};
//---------------------------//
else if (isdigit(nextc) && LastValidChar(json,i) == ':')
//-----------------修改----------------------------//
//---LastValidChar(i) --LastValidChar(json,i)-----//
else if (isdigit(nextc) && LastValidChar(i) == ':')
{
tokens = FetchNumStr(json, i, offset);
}
2.不支持数组的读取
测试demo
std::string data = "{\"type\": \"cdk\",\"cdk\":\"18000002\",\
\"frist_reward_goods\":[\
[\"1\",\"3037\",\"100\"],\
[\"2\",\"3037\",\"100\"],\
[\"3\",\"3037\",\"100\"],\
[\"4\",\"3037\",\"100\"]]}";
TinyJson在读取的时候,数组只显示为字符串,无论 tiny::xarray还是 tiny::xobject 都无法解析。后来就做了修改。
tiny::xarray arry;
get_value(j, std::string("frist_reward_goods"), arry);
结果如下图:
根据读取结果,处理结果无法使用。数组元素读取结果,把[]符号都加入进去还是读取很失败。
修改如下:
/*
函数名:字符串切割子函数
srcStr:源字符串
delimStr:分割符字符串
repeatedCharIgnored:是否支持分割符字符串合并
return:切割后的字符串Vector
*/
std::vector<std::string> splitString(std::string srcStr, std::string delimStr, bool repeatedCharIgnored)
{
std::vector<std::string> resultStringVector;
std::replace_if(srcStr.begin(), srcStr.end(), [&](const char& c) {if (delimStr.find(c) != std::string::npos) { return true; } else { return false; }}/*pred*/, delimStr.at(0));//将出现的所有分隔符都替换成为一个相同的字符(分隔符字符串的第一个)
size_t pos = srcStr.find(delimStr.at(0));
std::string addedString = "";
while (pos != std::string::npos) {
addedString = srcStr.substr(0, pos);
if (!addedString.empty() || !repeatedCharIgnored) {
resultStringVector.push_back(addedString);
}
srcStr.erase(srcStr.begin(), srcStr.begin() + pos + 1);
pos = srcStr.find(delimStr.at(0));
}
addedString = srcStr;
if (!addedString.empty() || !repeatedCharIgnored) {
resultStringVector.push_back(addedString);
}
return resultStringVector;
}
/*
字符串数组获取
in j:Json对象
int key:Json键值
output value 字符串数组
--利用 key,获取数组字符串。'['数组元素的开始,'],'为一个字符串数组的结束。判断'],'来划分数组。内部以','来划分,子相元素。
*/
static void get_value(tiny::TinyJson j, std::string key, std::vector<std::vector<std::string>> &value)
{
std::string object = j.Get<std::string>(key);
int i = 1;
while ( i < object.length() - 1)
{
std::vector<std::string> item;
if (object[i] == '[')
{
std::string item_string;
i++;
while (object[i] != ']' && object[i] != '[')
{
if (object[i] != ',' && object[i] != '"' && object[i] != ' ')
{
item_string += object[i];
}
else if (object[i] == ',' || ( object[i] == '"'&& object[i+1] == ']'))
{
item.push_back(item_string);
item_string = "";
}
i++;
}
}
if(!item.empty())
value.push_back(item);
i++;
}
}
处理结果如下所示:
std::vector<std::vector<std::string>> vec;
get_value(j, std::string("frist_reward_goods"), vec);
3.相连Key和value的获取
在实际使用中,发现相连的key和value如果相同,返回的value为下一个key值。
demo:type的值为cdk,cdk的值为18000002
"type\": \"cdk\",\"cdk\":\"18000002\"
std::string cdk;
get_value(j,std::string("cdk"),cdk);
处理结果如下图:
原因:
原处理函数为:
template<typename R>
R Get(std::string key, R defVal) {
auto itr = std::find(KeyVal_.begin(), KeyVal_.end(), key);
if (itr == KeyVal_.end()) {
return defVal;
}
return Value(*(++itr)).GetAs<R>();
}
使用了std::find()方法,find的定义为返回第一个找到的元素。这里并没有做返回的值是否为key还是value,所以出现了这个错误,修改如下,或元素的类型判断。根据json key和value一一对应的关系,可以判断value为偶数为,从0开始计数,value为奇数位。所以修改如下,手动遍历。
template<typename R>
R Get(std::string key, R defVal) {
auto itr = KeyVal_.begin();
int i = 0;
for (itr; itr != KeyVal_.end(); itr++)
{
if (key == (*itr) && !(i % 2))
break;
i++;
}
if (itr == KeyVal_.end()) {
return defVal;
}
return Value(*(++itr)).GetAs<R>();
}
运行结果如下:
cdk:18000002
封装
头文件调用
#include <iostream>
#include <string>
#include <vector>
#include "tinyjson.hpp"
//类的封装
class Json
{
public:
static tiny::TinyJson parse(std::string data)
{
tiny::TinyJson json;
json.ReadJson(data);
return json;
}
static void get_value(tiny::TinyJson j, std::string key, std::string &value)
{
value = j.Get<std::string>(key);
}
static void get_value(tiny::TinyJson j, std::string key, tiny::xarray &value)
{
value = j.Get<tiny::xarray>(key);
}
static void get_value(tiny::TinyJson j, std::string key, std::vector<std::vector<std::string>> &value)
{
std::string object = j.Get<std::string>(key);
int i = 1;
while ( i < object.length() - 1)
{
std::vector<std::string> item;
if (object[i] == '[')
{
std::string item_string;
i++;
while (object[i] != ']' && object[i] != '[')
{
if (object[i] != ',' && object[i] != '"' && object[i] != ' ')
{
item_string += object[i];
}
else if (object[i] == ',' || ( object[i] == '"'&& object[i+1] == ']'))
{
item.push_back(item_string);
item_string = "";
}
i++;
}
}
if(!item.empty())
value.push_back(item);
i++;
}
}
private:
};
模版函数
template<typename R>
void get_value(tiny::TinyJson j, std::string key, R &value)
{
Json::get_value(j, key, value);
}
使用:
//序列化
tiny::TinyJson j = Json::parse(data);
//普通元素
std::string cdk;
get_value(j,std::string("cdk"),cdk);
//list数组元素
tiny::xarray arry;
get_value(j, std::string("frist_reward_goods"), arry);
//vector数组元素
std::vector<std::vector<std::string>> vec;
get_value(j, std::string("frist_reward_goods"), vec);
其他
对TinyJson的再次封装,使使用足够方便。因为本人只使用解析,所以针对修改是针对TinyJson的解析修改。其他还需要其他人完善。
也可以将我上诉的元素再次封装于tinyjson.hpp.获取根据其他的使用再次封装。
想着上传合并到原作者的git分支,但是已经有两年没有更新就没有做。
如需源码请留言。