Json格式作为input文件格式,开源库nlohmann/json教程,包括静态变量
当我们有很多参数需要读入程序时,可以将这些参数写在一个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;
}
靓仔,你学费了吗?有问题评论区见~
参考: