JSON for Modern C++ 使用总结

本文详细介绍如何使用nlohmann JSON库进行C++中的JSON序列化与反序列化,包括基本使用、从文件读取JSON、类型转换及复杂数据结构处理,通过实例演示如何将JSON字符串转换为C++类。

 

最近需要用C++实现JSON的序列化与反序列化,在网上苦苦找寻,一开始相中了Jsoncpp,下载下来苦苦编译啊,设置是真心头疼,对于我这个小白来说是真麻烦呀。各种大神的各种经验完全不管用,最后碰巧看到了这个德国大神的巨作。就是我送给你们的玩具啦~~

首先引用两个中文 博客,因为我英文较差,看中文还比较轻松一些。所以从一开始接触这个库就是靠的这两个大神指引道路的。

废话不多说了,那两个博客写的比较详细了,但是我感觉我还是的把我经常用到的功能列出来,也可以补充一下前辈没有说明的东西。

 

一、国际惯例,使用背景

在自己的项目中添加头文件

#include "json.hpp"

using namespace std;
using json = nlohmann::json;

完美~

using json = nlohmann::json

使用C++风格的重命名

 

二、从文件中读取多个json对象

从文件中读取多个JSON对象并把每个对象中的每个key读出来

	json j;
	ifstream ifile("logService.json");
	if (ifile.fail()) {
		cout << "..." << endl;
		return 0;
	}
	string str;
	while (!ifile.eof())
	{
		std::getline(ifile, str);
		j = json::parse(str);
		cout << setw(4) << j << endl;
		cout << "++++++++++++++++++++++++++++++++";
		for (json::iterator i = j.begin(); i != j.end(); ++i)
		{
			
			cout << "key:"<<i.key()<< endl;
		}

	}

 

JSON.hpp中支持了STL的语法,可以用迭代器。

Json文件中不可以有换行符\n

 

更新一波吧,下面是json to class 的反序列化测试代码,序列化函数为 json.dump(),json库内的操作就不重复了。

#include "nlohmann/json.hpp"

using json = nlohmann::json;

enum class Type
{
    kType1 = 0,
    kType2 = 1,
    kType3 = 2,
    kType4 = 3,
    kType5 = 4
};

NLOHMANN_JSON_SERIALIZE_ENUM(Type, {
    {Type::kType1, nullptr},
    {Type::kType2, "stopped"},
    {Type::kType3, "running"},
    {Type::kType4, "completed"},
    {Type::kType5, "completed"},
    });

class TestType
{
public:
    NLOHMANN_DEFINE_TYPE_INTRUSIVE(TestType, test1, test2, test3, test4);
    int test1, test2, test3;
    Type test4;
};

class TestType2
{
public:
    NLOHMANN_DEFINE_TYPE_INTRUSIVE(TestType2, test21, test22, test23);
    int test21, test22;
    TestType test23;
};

class TestType3 :public TestType
{
public:
    NLOHMANN_DEFINE_TYPE_INTRUSIVE(TestType3, test1, test2, test3, test4, test31, test32);
    int test31, test32;
};

class TestType4 :public TestType
{
public:
    NLOHMANN_DEFINE_TYPE_INTRUSIVE(TestType4, test1, test2, test3, test4, test41, test42, test43);
    int test41, test42;
    TestType test43;
};

int main()
{
    json j = Type::kType2;
    assert(j == "stopped");

    //// json string to enum
    json j3 = "running";
    assert(j3.get<Type>() == Type::kType3);

    //// undefined json value to enum (where the first map entry above is the default)
    json jPi = 3.14;
    assert(jPi.get<Type>() == Type::kType1);

    json j2 = "{\"test1\":1,\"test2\":2,\"test3\":3,\"test4\":\"stopped\"}"_json;
    TestType test(j2);
    assert(test.test1 == 1);
    assert(test.test2 == 2);
    assert(test.test3 == 3);
    assert(test.test4 == Type::kType2);

    json j4 = "{\"test21\":21,\"test22\":22,\"test23\":{\"test1\":1,\"test2\":2,\"test3\":3,\"test4\":\"stopped\"}}"_json;
    TestType2 test2(j4);
    assert(test2.test21 == 21);
    assert(test2.test22 == 22);
    assert(test2.test23.test1 == 1);
    assert(test2.test23.test4 == Type::kType2);

    json j5 = "{\"test1\":1,\"test2\":2,\"test3\":3,\"test4\":\"stopped\",\"test31\":31,\"test32\":32}"_json;
    TestType3 test3(j5);
    assert(test3.test1 == 1);
    assert(test3.test2 == 2);
    assert(test3.test3 == 3);
    assert(test3.test4== Type::kType2);
    assert(test3.test31 == 31);
    assert(test3.test32 == 32);

    json j6 = "{\"test1\":1,\"test2\":2,\"test3\":3,\"test4\":\"stopped\",\"test41\":31,\"test42\":32,\"test43\":{\"test1\":1,\"test2\":2,\"test3\":3,\"test4\":\"stopped\"}}"_json;
    TestType4 test4(j6);
    assert(test4.test1 == 1);
    assert(test4.test2 == 2);
    assert(test4.test3 == 3);
    assert(test4.test4 == Type::kType2);
    assert(test4.test41 == 31);
    assert(test4.test42 == 32);
    assert(test4.test43.test1 == 1);
    assert(test4.test43.test4 == Type::kType2);
    return 0;
}

再额外写一点自己的代码吧~~

下面代码主要目的是为了让逻辑代码与Josn库解耦,就是万一想换Json库时只想改一个文件,这样的化其他取值的文件不能直接使用该Json 库,实际开发中也确实遇到了这样的情况,想换jsoncpp那个库,就改了一下里面的代码就可以啦~~

class config
{
    using json = nlohmann::json;
protected:
    config() = default;
    virtual ~config() = 0;
    bool LoadFromFile(std::string file_name);
    template <typename T>
    T GetValue(const std::string& key)
    {
        return json_data_.value(key, T{});
    }

    template <typename T>
    void GetValue(const std::string& key, T& value)
    {
        value = json_data_.value(key, value);
        return;
    }
private:
    std::string file_name_;
    json json_data_;
};

struct SiXXXXXXConf
{
    NLOHMANN_DEFINE_TYPE_INTRUSIVE(SiXXXXXXConf, ip_, port_, stxxxxxxxxer_
        , tuxxxxxxxxr_, xxxxxxxxxxxame_, xxxxxxxxxxxxntial_);
    SiXXXXXXConf()
        : ip_{ }, port_{ 0 }
        , stxxxxxxxxer_{}
        , tuxxxxxxxxr_{}, xxxxxxxxxxxame_{}, xxxxxxxxxxxxntial_{} {}
    std::string ip_;
    utint32 port_;
    std::string stxxxxxxxxer_;
    std::string tuxxxxxxxxr_;
    std::string xxxxxxxxxxxame_;
    std::string xxxxxxxxxxxxntial_;
};

class Webxxxxxxxig : public config
{
    using WConfig = std::variant<utint32, std::string, Rect, SiXXXXXXConf, ControllerConf, std::vector<std::string>, CodecConf>;
public:
    Webxxxxxxxig();
    Webxxxxxxxig(const std::string& config_file_name);
    Webxxxxxxxig(const Webxxxxxxxig& config);
    ~Webxxxxxxxig();
    bool LoadConfig()
    {
        ASSERT_EQUEL_RETURN(config_file_name_, "", false);
        ASSERT_NOTEQUEL_RETURN(LoadFromFile(config_file_name_), true, false);
        conxxxxxxxxtainer_["locaxxxxme_"] = GetValue<std::string>("locaxxxxme_");
        conxxxxxxxxtainer_["remoxxxxames_"] = GetValue<std::vector<std::string>>("remoxxxxames_");
        conxxxxxxxxtainer_["scrxxxxxxxze_"] = GetValue<Rect>("screen-size_");
        conxxxxxxxxtainer_["signaxxxxxxxnfig__"] = GetValue<SiXXXXXXConf>("signaxxxxxxxnfig__");
        conxxxxxxxxtainer_["controlxxxxxonfig_"] = GetValue<ControllerConf>("controlxxxxxonfig_");
    }
    bool LoadConfig(const std::string& config_file_name);
    template<typename T>
    T Get(const std::string key) const
    {
        T ret{};
        auto it = conxxxxxxxxtainer_.find(key);
        if (it != conxxxxxxxxtainer_.end()) ret = std::get<T>(it->second);
        return ret;
    }
    template<typename T>
    void Get(const std::string key, T& out_data) const
    {
        auto it = conxxxxxxxxtainer_.find(key);
        if (it != conxxxxxxxxtainer_.end()) out_data = std::get<T>(it->second);
    }
private:
    std::string config_file_name_;
    std::map<std::string, WConfig> conxxxxxxxxtainer_;
};

缺少了部分无关紧要的代码,小伙伴自己脑补吧~~值得注意的是 Variant 这个是C++17标准库中的联合变量,自己调整编译器的C++支持标准吧,vs2019是c++14起。

上面的WebrtcConfig 那个类主要目的就是把所有的配置从json 中读出来,然后放到容器中,为了让容器可以存储不同类型的变量,我使用了标准库中联合变量,取出的后就不再访问json 库了,取配置是直接访问容器,当然还没有写Json 文件的操作,后面加上再补吧,现在没有这个需要求。

 

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值