赞
踩
当我们有很多参数需要读入程序时,可以将这些参数写在一个input文件中,程序启动后读入。如果输入的参数还具有一定的层级关系而变得复杂时,那就可以考虑使用json格式的输入文件了。例如下面这样的输入:
{
"pi": 3.141,
"happy": true,
"name": "Niels",
"nothing": null,
"answer": {
"everything": 42
},
"list": [1, 0, 2],
"object": {
"currency": "USD",
"value": 42.99
}
}
本文推荐一个非常好用的json文件读写开源库,并介绍一些满足大多数人使用场景的基础用法。
开源库:nlohmann/json
,地址:https://github.com/nlohmann/json
安装方式非常简单,将库中的nlohmann
文件夹拷贝到/usr/include
中即可(linux操作系统)。
在主函数文件中引用头文件:
#include "nlohmann/json.hpp"
using json = nlohmann::json;
在讲怎样从文件中读取json数据之前,先说说开源库中定义的json对象。
定义json对象
// 定义json对象,像初始化c++map类型对象一样初始化json对象
json j;
j["pi"] = 3.14;
j["name"] = "Niels";
j["if_happy"] = true;
j["nothing"] = nullptr;
j["book"]["id"] = 12345;
j["book"]["price"] = 12.5;
j["list"] = {1,0,3};
j["object"] = {{"currency","usd"},{"value",22.5}};
此时json对象j
已经含有上述初始化的各个成员,成员的获取如下:
// 成员获取示例1
double num_pi;
num_pi = j["pi"].get<double>();
// 成员获取示例2
auto money = j["object"];
cout<<num_pi<<endl<<money<<endl;
c++ string对象与json对象的数据交换
// 使用string对象解析获得json对象,用到json::parse(string s)函数 string s; // 从json对象获得对象的内容并放入string对象中(术语叫序列化),数字4指的是对象输出的字符串换行的缩进为4 s = j.dump(4); cout<<s<<endl; // R开头的字符串表示字符串内容不做转义 s= R"( { "name":"Niels", "id":12345 } )"; // 从string对象中获得内容解析放入json对象中 j = json::parse(s); cout<<j.dump(2)<<endl;
json对象的文件读入与输出
// 终于到了文件了,这部分是大部分人需要用到的,就是从文件中读入json,或者输出json格式的文件
ofstream os{"out.plt"};
if(!os) cout<<"file open failed"<<endl;
// 输出到文件,可以直接用<<运算符结合stew(4)来输出,也可以先dump成string对象再输出
os<<j.dump(4)<<endl;
os.close();
ifstream is{"out.plt"};
if(!is) cout<<"input file open failed"<<endl;
// 从文件中读入
is>>j;
cout<<setw(4)<<j<<endl;
is.close();
枚举变量在json对象中怎么表示
由于默认情况下json把枚举序列化为整型,可能会出现错误,所以需要使用宏定义NLOHMANN_JSON_SERIALIZE_ENUM
来把枚举变量和string绑定在一起,且定义一个无效的枚举变量来防止错误,枚举这块比较绕,可以参考链接:https://json.nlohmann.me/features/enum_conversion/
// example enum type declaration enum class TaskState { TS_STOPPED, TS_RUNNING, TS_COMPLETED, TS_INVALID=-1, }; // map TaskState values to JSON as strings NLOHMANN_JSON_SERIALIZE_ENUM( TaskState, { {TaskState::TS_INVALID, nullptr}, {TaskState::TS_STOPPED, "stopped"}, {TaskState::TS_RUNNING, "running"}, {TaskState::TS_COMPLETED, "completed"}, }) s = R"({ "task":"running" })"; // json key:value对的value现在用宏定义绑定的string来赋值,json对象自动会将其匹配到对应的枚举变量 j = json::parse(s); TaskState task=j["task"]; cout<<static_cast<int>(task)<<endl;
用户自定义类对象与json对象的数据互传
如果用户自定义的类对象需要从json对象读入成员变量的值,或者从对象中抽出数据到json对象中,可以定义两个名为to_json()和from_json()的函数,即可直接使用 = 赋值运算符来操作json对象和用户自定义对象的互相转换了
class person{
public:
string name;
int age;
};
person p{"Taylor",18};
j = p;
cout<<j<<endl;
j["age"]= 20;
p = j;
cout<<"p.age = "<<p.age<<endl;
上面这个方法已经够简单了,但是Lohmann还提供了更方便的宏定义,只需使用
LOHMANN_DEFINE_TYPE_NON_INTRUSIVE(类名字,依次写出和json对象共有的成员变量)
即可,不用写出to_json()和from_json()。具体本文最后给出的示例参考文件开头部分的宏定义:
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(person, name, age);
全部代码如下:
/** * 编译命令:g++ -std=c++11 test.cpp -o xx */ #include <iostream> #include <fstream> #include <iomanip> #include <string> #include "nlohmann/json.hpp" using json=nlohmann::json; using namespace std; class person{ public: string name; int age; }; // 方式1 , 如果使用该方式,将下面 #if 0 改为 #if 1 #if 0 void to_json(json& j, const person& p) { j = json{{"name", p.name}, {"age",p.age}}; } void from_json(const json& j, person& p) { p = person{j["name"].get<string>(), j["age"].get<int>()}; } #else // 方式2 NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(person, name, age); #endif // example enum type declaration enum class TaskState { TS_STOPPED, TS_RUNNING, TS_COMPLETED, TS_INVALID=-1, }; // map TaskState values to JSON as strings NLOHMANN_JSON_SERIALIZE_ENUM( TaskState, { {TaskState::TS_INVALID, nullptr}, {TaskState::TS_STOPPED, "stopped"}, {TaskState::TS_RUNNING, "running"}, {TaskState::TS_COMPLETED, "completed"}, }) int main() { cout<<"--------------------- part 1 -------------------"<<endl; // 定义json对象,像初始化c++map类型对象一样初始化json对象 json j; j["pi"] = 3.14; j["name"] = "Niels"; j["if_happy"] = true; j["nothing"] = nullptr; j["book"]["id"] = 12345; j["book"]["price"] = 12.5; j["list"] = {1,0,3}; j["object"] = {{"currency","usd"},{"value",22.5}}; cout<<setw(4)<<j<<endl; // 成员获取示例1 double num_pi; num_pi = j["pi"].get<double>(); // 成员获取示例2 auto money = j["object"]; cout<<num_pi<<endl<<money<<endl; cout<<"--------------------- part 2 -------------------"<<endl; // 使用string对象解析获得json对象,用到json::parse(string s)函数 string s; // 从json对象获得对象的内容并放入string对象中(术语叫序列化),数字4指的是对象输出的字符串换行的缩进为4 s = j.dump(4); cout<<s<<endl; // R开头的字符串表示字符串内容不做转义 s= R"( { "name":"Niels", "id":12345 } )"; // 从string对象中获得内容解析放入json对象中 j = json::parse(s); cout<<j.dump(2)<<endl; cout<<"--------------------- part 3 -------------------"<<endl; // 终于到了文件了,这部分是大部分人需要用到的,就是从文件中读入json,或者输出json格式的文件 ofstream os{"out.plt"}; if(!os) cout<<"file open failed"<<endl; // 输出到文件,可以直接用<<运算符结合stew(4)来输出,也可以先dump成string对象再输出 os<<j.dump(4)<<endl; os.close(); ifstream is{"out.plt"}; if(!is) cout<<"input file open failed"<<endl; // 从文件中读入 is>>j; cout<<setw(4)<<j<<endl; is.close(); // 如果用户自定义的类对象需要从json对象读入成员变量的值,或者从对象中抽出数据到json对象中,可以定义两个名为 // to_json()和from_json()的函数,即可直接使用 = 赋值运算符来操作json对象和用户自定义对象的互相转换了 person p{"Taylor",18}; j = p; cout<<j<<endl; j["age"]= 20; p = j; cout<<"p.age = "<<p.age<<endl; /* 上面这个方法已经够简单了,但是Lohmann还提供了更方便的宏定义,只需使用 NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(类名字,依次写出和json对象共有的成员变量);即可不用写出to_json()和from_json() 具体示例参考文件开头部分的宏定义*/ cout<<"--------------------- part 4 -------------------"<<endl; /* 最后一个部分了,讲一下枚举变量在json对象中的用法 由于默认情况下json把枚举序列化为整型,可能会出现错误,所以需要使用宏定义来把枚举变量和string绑定在一起,且定义一个 无效的枚举变量来防止错误*/ // 枚举这块比较绕,可以参考链接:https://json.nlohmann.me/features/enum_conversion/ s = R"({ "task":"running" })"; // json key:value对的value现在用宏定义绑定的string来赋值,json对象自动会将其匹配到对应的枚举变量 j = json::parse(s); TaskState task=j["task"]; cout<<static_cast<int>(task)<<endl; return 0; }
靓仔,你学费了吗?有问题评论区见~
参考:
https://blog.csdn.net/u011341856/article/details/108797920
https://json.nlohmann.me/features/enum_conversion/
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。