援引百度百科:json是一种轻量级的数据交换格式。对于习惯了python语言的我来说,json可以和内置的dict(字典)数据类型完美衔接, 所以就想着能不能横向推广到C++。 在GitHub上一搜索,发现star最高的nlohmann;主要的用法工程的readme其实已经说的很清楚了。
但是翻了很多博客,发现对于json 对象建模以及其相互转换的文档不多。此篇文章主要介绍读取json文件以及对象建模。希望对大家有所帮助!
一:将nlohmann库添加到自己的工程
使用nlohmann,我们只需将json.hpp头文件添加到自己的代码中(这个超级Nice)
#include <json.hpp>
// 为了使用方便,可以让json命名空间内所有标识符在此文件中可见
using json = nlohmann::json;
二:简单json字符串序列化/反序列化
-
反序列化成json
现有json字符串格式如下
{ "pi":3.1415, "happy":true }
将其反序列化成json对象
//方式1:在序列化后字符串后后添加 _json后缀 json j = "{ \"happy\": true, \"pi\": 3.141 }"_json; //方式2:从原生字符串 auto j2 = R"( { "happy": true, "pi": 3.141 } )"_json; //方式3:显示的调用 json::parse auto j3 = json::parse(R"({"happy": true, "pi": 3.141})"); //方式4:从json文件中读取 std::ifstream ifs("test.json"); json jf = json::parse(ifs);
-
序列化成json格式字符串
// 显示的转换 std::string s = j.dump(); // {"happy":true,"pi":3.141}
-
读取json文件
std::ifstream ifs("test.json"); json jf = json::parse(ifs);
-
保存成json文件
jsonf jsonfile; jsonfile["happy"] = true; jsonfile["pi"] = 3.141; std::ofstream file("test.json"); file << jsonfile;
三:较复杂的嵌套json字符串数据对象建模
现有json字符串格式如下:
{
"teacher_name": "wangwu",
"student_list":
[
{
"name": "zhangsan",
"age": 16,
"phone": "12345678"
},
{
"name": "lisi",
"age": 17,
"phone": "123456"
}
]
}
对于较为复杂的嵌套json字符串,通用的做法是对其进行建模,对应到C++则是结构体/类
-
json字符串分析
直观的看的话, 此json字符串包含两种两大部分:teacher_name和student_list, 其中student_list是一个数组。student_list嵌套的内容包含name、age和phone3个字段
-
建模
-
最里层的表示学生对象的json字符串可转成对应的 student 类/结构体
class student { public: string name; int age; string phone; };
-
外层的表示班主任及学生列表的对象可转换成对应的***class_room***类/结构体, 其中学生列表可以用vector容器表示
class class_room { public: string teacher_name; std::vector<student> student_list; };
-
为了能够对这两个模型进行自由转换,需要对每个类/结构体分别提供两个函数
// 对于student对象 void from_json(const json& j, student& p) { j.at("name").get_to(p.name); j.at("age").get_to(p.age); j.at("phone").get_to(p.phone); } void to_json(json& j, const student& s) { j = json{ { "name", s.name },{ "age",s.age },{ "phone", s.phone } }; } //对于class_room对象 void from_json(const json& j, class_room& p) { j.at("teacher_name").get_to(p.teacher_name); for (auto &student_t : j["student_list"]) { student s; from_json(student_t, s); p.student_list.push_back(s); } } void to_json(json& j, const class_room& s) { j = json{ {"teacher_name", s.teacher_name } }; for (auto & student_t : s.student_list) { json j_student; to_json(j_student, student_t); j["student_list"].push_back(j_student); } }
当使用class_room字符串调用json的构造函数时,相应的to_json函数就会被调用,调用完成后对应的json对象就解析完成。反过来,如果调用***get***或者***get_to***时,from_json方法将会被调用
-
注意事项:
- 上面3步的都应包含在同一个namespace中,否则nlohmann库将不能正确的进行转换。代码结构示例如下
namespace ns { class A { // 一些结构体变量 } void from_json(const json& j, A& a) { // 转换的代码 } void to_json(json& j, const A& a) { // 转换的代码 } }
- 需要用到这些转换的功能时,相应的头文件必须被包含,否则也不能转换成功
- 访问json的元素时,尽量使用 at() 方法而不要使用 ***operator[]***, 因为如果当有key不存在时,at() 方法会抛出异常而 operator[] 不会
-
-
json字符串转换成数据对象
ns::class_room cr; ns::from_json(commands, cr); //接下来就可以针对对象进行操作了 std::cout << cr.student_list[0].age << std::endl; cr.student_list[0].age += 10;
-
数据对象转换
json j_2; ns::class_room cr; ns::to_json(j_2, cr);
关注公众号【兰亭汇】即可获取完整代码~
感兴趣的同学可以看看。公众号会不定时分享工作生活中的所见所闻~