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对象。

  1. 定义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;
    
  2. 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;
    
  3. 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();
    
  4. 枚举变量在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;
    
  5. 用户自定义类对象与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 
#include 
#include 
#include 
#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/

你可能感兴趣的:(c++,json,c++)