当前位置:   article > 正文

惊喜!C++ 现代化json库nlohmann更高效的使用方式_nlohmann 结构体向量

nlohmann 结构体向量

背景

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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

最早我们使用这个库的用法是这样的,给每个结构体定义它的 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)
        {}
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

这种方法的优点是可以自己控制每个字段,比如字段名和结构体成员变量名可以不同,字段解析时可以增加自己的处理逻辑等等。但对于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__)) }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

可以看到,这个库是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)
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
namespace ns {
    class address {
      private:
        std::string street;
        int housenumber;
        int postcode;

      public:
        NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(address, street, housenumber, postcode)
    };
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

在这里插入图片描述

关注公众号 QTShared,带你探索更多QT相关知识。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/菜鸟追梦旅行/article/detail/601190
推荐阅读
相关标签
  

闽ICP备14008679号