惊喜!C++ 现代化json库nlohmann更高效的使用方式

2023-12-13 05:37:15

背景

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相关知识。

文章来源:https://blog.csdn.net/u012534831/article/details/134958660
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。