背景
nlohmann 这个库其实早在2019年项目中已经开始使用了,没有问题,这些库一般都不会进行升级。 最近在新的项目中也需要用Json解析,然后再去它的官网上过了一遍ReadMe,发现了一些提高效率的新功能。
链接:https://github.com/nlohmann/json
using json = nlohmann::json;
namespace ns {
void to_json(json& j, const person& p) {
j = json{{"name", p.name}, {"address", p.address}, {"age", p.age}};
}
void from_json(const json& j, person& p) {
j.at("name").get_to(p.name);
j.at("address").get_to(p.address);
j.at("age").get_to(p.age);
}
} // namespace ns
最早我们使用这个库的用法是这样的,给每个结构体定义它的 to_json 和 from_json 方法,给调用者提供模板函数来使用,借助 ADL 实现 to_json 和 from_json 的查找和调用,代码如下:(写这篇文章的时候发现这儿也用到了ADL技术,上篇文章:【建议收藏】QT实现字符串和枚举的相互转换,从源码角度分析实现原理: 也提到了,感兴趣的可以看看)。
namespace ns {
template<class T>
void to_string(const T &data, std::string &content)
{
json j;
to_json(j, data);
content = j.dump();
}
template<class T>
void from_string(const std::string &content, T &data)
{
try
{
json j = json::parse(content);
from_json(j, data);
}
catch (exception* e)
{}
}
}
这种方法的优点是可以自己控制每个字段,比如字段名和结构体成员变量名可以不同,字段解析时可以增加自己的处理逻辑等等。但对于90%的使用场景来说,只想要一个简单的结构体解析,上面的方法就显得特别的繁琐。
新发现
看了 nlohmann 新的ReadMe之后,发现它提供了几个宏,很方便的就能实现结构体的json序列化和反序列化。
@brief macro
@def NLOHMANN_DEFINE_TYPE_INTRUSIVE
@since version 3.9.0
*/
#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__)) }
#define NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(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) { Type nlohmann_json_default_obj; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) }
/*!
@brief macro
@def NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE
@since version 3.9.0
*/
#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Type, ...) \
inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \
inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) }
#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(Type, ...) \
inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \
inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { Type nlohmann_json_default_obj; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) }
可以看到,这个库是3.9.0版本(2020年)才增加了这几个宏,而我们最早使用是在2019年,还没有这么简单的用法 -、-
解释:
NLOHMANN_DEFINE_TYPE_INTRUSIVE(name, member1, member2, ...)
这个宏需要定义在结构体之内,它可以访问结构体/类的私有成员。NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(name, member1, member2, ...)
这个宏需要定义在结构体之外,但需要和结构体在同一个命名空间,但不能访问结构体的私有成员,因此被序列化的字段都需要定义成public。
所以,如果你的类没有私有成员,用NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE
就行了。如果你的类区分了私有成员和公有成员,用NLOHMANN_DEFINE_TYPE_INTRUSIVE
就行了。
这两个宏后面带有 WITH_DEFAULT
的宏的意思是当字段不存在,是否使用默认值填充。
下面是代码示例,分别展示这两个宏的用法:
namespace ns
{
struct HardWare {
int index = 1;
std::string type = "AMD";
std::string version = "0.0.1";
};
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(HardWare, index, type, version)
struct Device {
std::vector<HardWare> hardwarelist;
};
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(Device, hardwarelist)
}
namespace ns {
class address {
private:
std::string street;
int housenumber;
int postcode;
public:
NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(address, street, housenumber, postcode)
};
}
关注公众号 QTShared,带你探索更多QT相关知识。