C++json库nlohmannjson使用介绍

1、简介

最近项目中需要使用C++ Json序列化和反序列化,顺便调研了下目前比较好用的C++ json库,发现nlohmann/json应该是其中相对较好的json库。nlohmann/json有如下主要优点:

1、语法比较直观,类似于Python。

2、要使用nlohmann/json,只需要引入头文件json.hpp。无需引入lib之类的。

#include 

// for convenience
using json = nlohmann::json;

3、经过非常多的测试,代码质量非常高,没有内存泄漏。

4、内存效率、速度相对其它库较高。

2、实例介绍

2.1 创建Json对象

使用nlohmann/json创建Json对象,无需关心值类型,比如创建如下Json对象:

// create an empty structure (null)
json j;

// add a number that is stored as double (note the implicit conversion of j to an object)
j["pi"] = 3.141;

// add a Boolean that is stored as bool
j["happy"] = true;

// add a string that is stored as std::string
j["name"] = "Niels";

// add another null object by passing nullptr
j["nothing"] = nullptr;

// add an object inside the object
j["answer"]["everything"] = 42;

// add an array that is stored as std::vector (using an initializer list)
j["list"] = { 1, 0, 2 };

// add another object (using an initializer list of pairs)
j["object"] = { {"currency", "USD"}, {"value", 42.99} };

// instead, you could also write (which looks very similar to the JSON above)
json j2 = {
  {"pi", 3.141},
  {"happy", true},
  {"name", "Niels"},
  {"nothing", nullptr},
  {"answer", {
    {"everything", 42}
  }},
  {"list", {1, 0, 2}},
  {"object", {
    {"currency", "USD"},
    {"value", 42.99}
  }}
};
2.2 序列/反序列化

在class/struct内部定义NLOHMANN_DEFINE_TYPE_INTRUSIVE(name, member1, member2, …)

class Address
{
	std::string street;
	int housenumber;
	int postcode;
public:
	NLOHMANN_DEFINE_TYPE_INTRUSIVE(Address, street, housenumber, postcode)
};
字符串转成class/struct
std::string strTemp =(R"({"street": "test", "housenumber": 0, "postcode": 0})";
Address temp = nlohmann::json::parse(strTemp).get
();
class/struct转成字符串
Addree temp{"Test", 0, 0};
nlohmann::json jsonTemp = temp;
std::string strTemp = jsonTemp.dump();

为了方便序列/反序列化,可以封装下面两个接口:

template
   static void deserialize(const std::string &str, T &value)
   {
       if (str.size() <= 0)
           return;
       value = parse(str).template get();
   }
template
    static void serialize(const T &value, std::string &str)
    {
        str = json(value).dump();
    }

3、遇到的问题及解决办法

3.1 strings参数比class/struct的参数少,反序列化崩溃

实际项目中,json使用未必非常标准,有解析strings参数比较少的需求。比如2.2中类Address

std::string strTemp =(R"({"street": "test", "housenumber": 0})";
Address temp = nlohmann::json::parse(strTemp).get
();

strTemp少了postcode,执行第二行代码就会崩溃。

解决办法

打开nlohmann/json.hpp找到源码

#define NLOHMANN_JSON_FROM(v1) nlohmann_json_j.at(#v1).get_to(nlohmann_json_t.v1); 

将其改成

#define NLOHMANN_JSON_FROM(v1) if(nlohmann_json_j.contains(#v1)){nlohmann_json_j.at(#v1).get_to(nlohmann_json_t.v1);}

相比源码,就加了contains的判断,若待解析的json没有对应字段,就不作处理。

3.2 strings包含中文、“\"等反序列化崩溃

strings包含中文、“\“等“特殊字符”,用nlohmann::json反序列化就会崩溃。跟大多数json库一样,nlohmann::json是不支持中文、”\"等“特殊字符”的,因此需要先替换strings中的特殊字符,再反序列化。

3.3 类型必须匹配,数字无法转成string

实际项目中协议定的某些字段,经常出现数字、string混用的情况。nlohmann/json默认实现要求反序列化的目标class/struct参数类型和strings中完美匹配,否则解析过程中崩溃。

解决办法

原代码

template
void from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s)
{
    if (JSON_HEDLEY_UNLIKELY(!j.is_string()))
    {
         JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()), j)); 
    }
    s = *j.template get_ptr();
}

改造后

template
void from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s)
{
    if (JSON_HEDLEY_UNLIKELY(!j.is_string()))
    {
        /* 为了提高健壮性,对于json中非string的类型,也将其转成string */
        s = to_string(j);
        return;
        /* JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()), j)); */
    }
    s = *j.template get_ptr();
}
3.4 NLOHMANN_DEFINE_TYPE_INTRUSIVE不支持class/struct参数过多(超过63个)

NLOHMANN_DEFINE_TYPE_INTRUSIVE并不能无限展开,nlohmann/json默认支持63个参数。

解决方案

NLOHMANN_DEFINE_TYPE_INTRUSIVE的定义如下:

#define NLOHMANN_DEFINE_TYPE_INTRUSIVE(Type, ...)  \
    friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \
    friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) }

NLOHMANN_DEFINE_TYPE_INTRUSIVE的原理就是用NLOHMANN_JSON_EXPAND,1个NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, …))可以支持63个,超过可以另起1个,如下:

struct StructTest
{
	std::string param1;
	std::string param2;
	...
	std::string param63;
	std::string param64;
	...

    friend void from_json(const nlohmann::json& nlohmann_json_j, StructTest& nlohmann_json_t)
    {
        NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(func, param1, param2, ..., param63));
		NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(func, param64, param65, ...));
    }
    friend void to_json(nlohmann::json& nlohmann_json_j, const StructTest& nlohmann_json_t)
    {
        NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(func, param1, param2, ..., param63));
		NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(func, param64, param65, ...));
    }
};

你可能感兴趣的:(c++,json,开发语言)