栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

Modern C++ JSON nlohmann::json 使用详解

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Modern C++ JSON nlohmann::json 使用详解

git repo:https://github.com/nlohmann/json

nlohmann::json只需要一个头文件json.hpp, 不需要编译成lib, 直接放到项目中即可使用

下载json.hpp

在右侧找到release

点击进去后,下载json.hpp即可

代码示例

例如下面代码


#include 
#include "json.hpp"


using namespace std;


using json_t = nlohmann::json;

int main()
{
	json_t j;                                   // JSON对象

	j["age"] = 23;                              // "age":23
	j["name"] = "spiderman";                    // "name":"spiderman"
	j["gear"]["suits"] = "2099";                // "gear":{"suits":"2099"}
	j["jobs"] = { "superhero" };                // "jobs":["superhero"]  

	vector v = { 1,2,3 };                  // vector容器
	j["numbers"] = v;                           // "numbers":[1,2,3]

	map m =                 // map容器
	{ {"one",1}, {"two", 2} };           // 初始化列表 
	j["kv"] = m;                         // "kv":{"one":1,"two":2}

	cout << "--------序列化--------" << endl;

	string str1 = j.dump();
	string str2 = j.dump(2);
	cout << str1 << endl;         // 序列化,无缩进
	cout << str2 << endl;         // 序列化,有缩进,2个空格

	cout << "--------反序列化--------" << endl;

	// JSON文本,原始字符串,不要在R的行里面加注释,写了会被带到字符串里,导致parse异常
	string str = R"({                
		"name": "peter",
		"age" : 23,  
		"married" : true   
	})";

	try 
	{
		auto j = json_t::parse(str);  // 从字符串反序列化
		assert(j["age"] == 23);       // 验证序列化是否正确
		assert(j["name"] == "peter");
		cout << j["age"] << endl;
		cout << j["name"] << endl;
	}
	catch(std::exception& e) // 捕获异常
	{ 
		cout << "json反序列化失败" << endl;
		cout << e.what() << endl;
	}

    return 0;
}



输出结果

注意在反序列化时,可能会抛异常,例如上面使用C++ 11 R字符串,不要在R的行里面加注释,写了会被带到字符串里,导致parse异常;将str改成下面这种形式

string str = R"({                
		"name": "peter",
		"age" : 23,   //年龄
		"married" : true   
	})";

输出结果

--------反序列化--------
json反序列化失败
[json.exception.parse_error.101] parse error at line 3, column 17: syntax error while parsing object key - invalid literal; last read: ‘23, /’; expected string literal

代码说明 dump

dump() 可以序列化,返回值是string,得到它的 JSON 文本形式。默认的格式是紧凑输出,没有缩进,如果想要更容易阅读的话,可以加上指示缩进的参数:

string str1 = j.dump();
string str2 = j.dump(2);
cout << str1 << endl;         // 序列化,无缩进
cout << str2 << endl;         // 序列化,有缩进,2个空格

在进行http请求时,经常需要用到Raw data json, 那么此时就可以使用dump方法转成string, 传到http header.

parse

静态成员函数 parse() 用来对string进行反序列化,直接得到 JSON 对象,而且可以用 auto 自动推导类型。

json数组以及嵌套json解析

例如下面的json,包含了json嵌套,json数组

{
	"id":"2021",
	"name":{"EnglishName":"JackMa"},
	"title":[
		{
			"company":"taobao", 
			"position" : "AAA", 
			"salary" : "10000"
		},
		{ 
			"company":"zhifubao",
			"position" : "CCC",
			"salary" : "890898" 
		},
		{ 
			"company":"pingtouge",
			"position" : "KKK",
			"salary" : "76843" 
		}
	]
}

解析代码如下:

#include 
#include 
#include "json.hpp"
#include 

using namespace std;

using json = nlohmann::json;

int main()
{
	string str = R"({
		"id":"2021",
		"name":{"EnglishName":"JackMa"},
		"title":[
			{
				"company":"taobao", 
				"position" : "AAA", 
				"salary" : "10000"
			},
			{ 
				"company":"zhifubao",
				"position" : "CCC",
				"salary" : "890898" 
			},
			{ 
				"company":"pingtouge",
				"position" : "KKK",
				"salary" : "76843" 
			}
		]
	})";

	try
	{
		auto j = json::parse(str);  // 从字符串反序列化
		cout << j["id"] << endl;

		// 嵌套解析
		cout << j["name"]["EnglishName"] << endl;
		
		// 解析数组
		int _count = j["title"].size();
		for (int i = 0; i < _count; i++)
		{
			auto json_temp = j["title"][i];
			cout << json_temp["company"] << endl;
			cout << json_temp["position"] << endl;
			cout << json_temp["salary"] << endl;
		}
	}
	catch (std::exception& e) // 捕获异常
	{
		cout << "json反序列化失败" << endl;
		cout << e.what() << endl;
	}

	return 0;
}

结果如下:
“2021”
“JackMa”
“taobao”
“AAA”
“10000”
“zhifubao”
“CCC”
“890898”
“pingtouge”
“KKK”
“76843”

使用说明
  • 如果json的value还是一个json, 那么就用j["name"]["EnglishName"] 这种形式获取值;
  • 如何json value是数组,那么用size()获取个数,再用for循环依次用数组取值的方式获取单个数组里的值,代码如下:
// 解析数组
int _count = j["title"].size();
for (int i = 0; i < _count; i++)
{
	auto json_temp = j["title"][i];
	cout << json_temp["company"] << endl;
	cout << json_temp["position"] << endl;
	cout << json_temp["salary"] << endl;
}
中文异常处理

如果json字符串中包含有中文该如何处理呢,例如将上面的字符串中写一些中文,如下所示:

{
		"id":"2021",
		"name":{"EnglishName":"JackMa"},
		"title":[
			{
				"company":"taobao", 
				"position" : "程序员", 
				"salary" : "10000"
			},
			{ 
				"company":"zhifubao",
				"position" : "大股东",
				"salary" : "890898" 
			},
			{ 
				"company":"pingtouge",
				"position" : "老板",
				"salary" : "76843" 
			}
		]
	}

此时会抛异常,如下所示:

json反序列化失败
[json.exception.parse_error.101] parse error at line 7, column 19: syntax error while parsing value - invalid string: ill-formed UTF-8 byte; last read: '"?

根据提示可知,需要把字符串转化成utf8格式,代入如下:

#include 
#include 
#include "json.hpp"
#include 

using namespace std;

using json = nlohmann::json;

std::string String_TO_UTF8(std::string str)
{
	int nwLen = ::MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, NULL, 0);
	wchar_t* pwBuf = new wchar_t[nwLen + 1];
	ZeroMemory(pwBuf, nwLen * 2 + 2);

	::MultiByteToWideChar(CP_ACP, 0, str.c_str(), str.length(), pwBuf, nwLen);

	int nLen = ::WideCharToMultiByte(CP_UTF8, 0, pwBuf, -1, NULL, NULL, NULL, NULL);

	char* pBuf = new char[nLen + 1];
	ZeroMemory(pBuf, nLen + 1);

	::WideCharToMultiByte(CP_UTF8, 0, pwBuf, nwLen, pBuf, nLen, NULL, NULL);

	std::string retStr(pBuf);

	delete[]pwBuf;
	delete[]pBuf;

	pwBuf = NULL;
	pBuf = NULL;

	return retStr;
}

std::string UTF8_To_String(const std::string& s)
{
	if (s.empty())
	{
		return std::string();
	}

	std::wstring result;

	int n = MultiByteToWideChar(CP_UTF8, 0, s.c_str(), -1, NULL, 0);
	wchar_t* buffer = new wchar_t[n];

	::MultiByteToWideChar(CP_UTF8, 0, s.c_str(), -1, buffer, n);

	result = buffer;
	delete[] buffer;

	std::string result2;
	int len = WideCharToMultiByte(CP_ACP, 0, result.c_str(), result.size(), NULL, 0, NULL, NULL);
	char* buffer2 = new char[len + 1];
	WideCharToMultiByte(CP_ACP, 0, result.c_str(), result.size(), buffer2, len, NULL, NULL);
	buffer2[len] = '';
	result2.append(buffer2);
	delete[] buffer2;

	return result2;
}



int main()
{
	string str = R"({
		"id":"2021",
		"name":{"EnglishName":"JackMa"},
		"title":[
			{
				"company":"taobao", 
				"position" : "程序员", 
				"salary" : "10000"
			},
			{ 
				"company":"zhifubao",
				"position" : "大股东",
				"salary" : "890898" 
			},
			{ 
				"company":"pingtouge",
				"position" : "老板",
				"salary" : "76843" 
			}
		]
	})";

	// 数据源转成utf8
	string u8str = String_TO_UTF8(str);

	try
	{
		auto j = json::parse(u8str);  // 从字符串反序列化
		cout << j["id"] << endl;

		// 嵌套解析
		cout << j["name"]["EnglishName"] << endl;
		
		// 解析数组
		int _count = j["title"].size();
		for (int i = 0; i < _count; i++)
		{
			auto json_temp = j["title"][i];
			cout << json_temp["company"] << endl;
			
			// utf8结果转string
			cout << UTF8_To_String(json_temp["position"]) << endl;
			cout << json_temp["salary"] << endl;
		}
	}
	catch (std::exception& e) // 捕获异常
	{
		cout << "json反序列化失败" << endl;
		cout << e.what() << endl;
	}

	return 0;
}

要注意这两行代码:

// 数据源转成utf8
string u8str = String_TO_UTF8(str);

// utf8结果转string
cout << UTF8_To_String(json_temp["position"]) << endl;

数据源包含中文,会抛异常,转成utf8就ok了;解析出来的结果是utf8格式,如果不做转换,直接输出也会抛异常,终止解析。

其它用法说明 自定义数据源
#include "doctest_compatibility.h"

#include 
using nlohmann::json;

#include 

namespace
{
TEST_CASE("Use arbitrary stdlib container")
{
    std::string raw_data = "[1,2,3,4]";
    std::list data(raw_data.begin(), raw_data.end());

    json as_json = json::parse(data.begin(), data.end());
    CHECK(as_json.at(0) == 1);
    CHECK(as_json.at(1) == 2);
    CHECK(as_json.at(2) == 3);
    CHECK(as_json.at(3) == 4);
}

struct MyContainer
{
    const char* data;
};

const char* begin(const MyContainer& c)
{
    return c.data;
}

const char* end(const MyContainer& c)
{
    return c.data + strlen(c.data); // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
}

TEST_CASE("Custom container non-member begin/end")
{

    MyContainer data{"[1,2,3,4]"};
    json as_json = json::parse(data);
    CHECK(as_json.at(0) == 1);
    CHECK(as_json.at(1) == 2);
    CHECK(as_json.at(2) == 3);
    CHECK(as_json.at(3) == 4);

}

TEST_CASE("Custom container member begin/end")
{
    struct MyContainer2
    {
        const char* data;

        const char* begin() const
        {
            return data;
        }

        const char* end() const
        {
            return data + strlen(data); // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
        }
    };

    MyContainer2 data{"[1,2,3,4]"};
    json as_json = json::parse(data);
    CHECK(as_json.at(0) == 1);
    CHECK(as_json.at(1) == 2);
    CHECK(as_json.at(2) == 3);
    CHECK(as_json.at(3) == 4);
}

TEST_CASE("Custom iterator")
{
    const char* raw_data = "[1,2,3,4]";

    struct MyIterator
    {
        using difference_type = std::size_t;
        using value_type = char;
        using pointer = const char*;
        using reference = const char&;
        using iterator_category = std::input_iterator_tag;

        MyIterator& operator++()
        {
            ++ptr; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
            return *this;
        }

        reference operator*() const
        {
            return *ptr;
        }

        bool operator!=(const MyIterator& rhs) const
        {
            return ptr != rhs.ptr;
        }

        const char* ptr;
    };

    // avoid -Wunused-local-typedefs
    CHECK(std::is_same::value);
    CHECK(std::is_same::value);
    CHECK(std::is_same::value);
    CHECK(std::is_same::value);
    CHECK(std::is_same::value);

    MyIterator begin{raw_data};
    MyIterator end{raw_data + strlen(raw_data)}; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)

    json as_json = json::parse(begin, end);
    CHECK(as_json.at(0) == 1);
    CHECK(as_json.at(1) == 2);
    CHECK(as_json.at(2) == 3);
    CHECK(as_json.at(3) == 4);
}

} // namespace
SAX interface

sax的接口,包括以下函数:

// called when null is parsed
bool null();

// called when a boolean is parsed; value is passed
bool boolean(bool val);

// called when a signed or unsigned integer number is parsed; value is passed
bool number_integer(number_integer_t val);
bool number_unsigned(number_unsigned_t val);

// called when a floating-point number is parsed; value and original string is passed
bool number_float(number_float_t val, const string_t& s);

// called when a string is parsed; value is passed and can be safely moved away
bool string(string_t& val);
// called when a binary value is parsed; value is passed and can be safely moved away
bool binary(binary_t& val);

// called when an object or array begins or ends, resp. The number of elements is passed (or -1 if not known)
bool start_object(std::size_t elements);
bool end_object();
bool start_array(std::size_t elements);
bool end_array();
// called when an object key is parsed; value is passed and can be safely moved away
bool key(string_t& val);

// called when a parse error occurs; byte position, the last token, and an exception is passed
bool parse_error(std::size_t position, const std::string& last_token, const detail::exception& ex);

可以在github查看使用方法,或者使用cmake编译,查看例子代码。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/931354.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号