zl程序教程

您现在的位置是:首页 >  后端

当前栏目

C++轻量级Json解析工具—TinyJson

C++工具JSONJSON 解析 轻量级
2023-06-13 09:17:23 时间

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分支,但是已经有两年没有更新就没有做。

如需源码请留言。