当前位置:   article > 正文

C++json库nlohmannjson使用介绍_nlohmann::json

nlohmann::json

1、简介

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

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

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

#include <nlohmann/json.hpp>

// for convenience
using json = nlohmann::json;
  • 1
  • 2
  • 3
  • 4

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}
  }}
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
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)
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
字符串转成class/struct
std::string strTemp =(R"({"street": "test", "housenumber": 0, "postcode": 0})";
Address temp = nlohmann::json::parse(strTemp).get<Address>();
  • 1
  • 2
class/struct转成字符串
Addree temp{"Test", 0, 0};
nlohmann::json jsonTemp = temp;
std::string strTemp = jsonTemp.dump();
  • 1
  • 2
  • 3

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

template<class T>
   static void deserialize(const std::string &str, T &value)
   {
       if (str.size() <= 0)
           return;
       value = parse(str).template get<T>();
   }
template<class T>
    static void serialize(const T &value, std::string &str)
    {
        str = json(value).dump();
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

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<Address>();
  • 1
  • 2

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

解决办法

打开nlohmann/json.hpp找到源码

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

将其改成

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

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

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

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

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

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

解决办法

原代码

template<typename BasicJsonType>
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<const typename BasicJsonType::string_t*>();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

改造后

template<typename BasicJsonType>
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<const typename BasicJsonType::string_t*>();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
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__)) }
  • 1
  • 2
  • 3

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, ...));
    }
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Gausst松鼠会/article/detail/595687
推荐阅读
相关标签
  

闽ICP备14008679号