json库nlohmann简单使用教程,快速入手,完成json对象的构建,从STL构造json,以及序列化和反序列化操作,二进制写入、读取本地数据

本文主要是介绍json库nlohmann简单使用教程,快速入手,完成json对象的构建,从STL构造json,以及序列化和反序列化操作,二进制写入、读取本地数据,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

首先你可能需要了解一下JSON格式,它实际上还是比较简单的一种格式,理解起来还是很容易的,如果你对它不熟悉,可以参考这份教程快速学习一下:JSON 教程

1. 定义JSON数值类型

如果你想要创建一个如下这样形式的JSON对象:

{"pi": 3.141,"happy": true,"name": "Niels","nothing": null,"answer": {"everything": 42},"list": [1, 0, 2],"object": {"currency": "USD","value": 42.99}
}

使用nlohmann库可以非常方便的完成。

json j; //首先创建一个空的json对象
j["pi"] = 3.141; //然后通过名称/值对的方式进行初始化,此时名称"pi"对应的数值就是3.141
j["happy"] = true;//将名称"happy"赋值为true
j["name"] = "Niels";//将字符串"Niels"存储到"name"
j["nothing"] = nullptr;//"nothing"对应的是空指针
j["answer"]["everything"] = 42;//对对象中的对象进行初始化
j["list"] = { 1, 0, 2 };//使用列表初始化的方法对"list"数组初始化
j["object"] = { {"currency", "USD"}, {"value", 42.99} };//对对象进行初始化

注意: 在进行如上方式构造JSON对象时,你并不需要告诉编译器你要使用哪种类型,nlohmann会自动进行隐式转换。

如果你想显式定义或者表达一些情况,可以考虑如下这样的方法:

json empty_array_explicit = json::array();//初始化一个JSON格式的空数组
json empty_object_implicit = json({});//隐式定义一个空对象
json empty_object_explicit = json::object();//显式定义一个空对象
json array_not_object = json::array({ {"currency", "USD"}, {"value", 42.99} });//显式定义并初始化一个JSON数组

2.从STL容器转换到json

nlohmann库支持从STL的任意序列容器初始化获得json对象(std::array, std::vector, std::deque, std::forward_list, std::list),它们的值可以被用来构造json的值。

std::set, std::multiset, std::unordered_set, std::unordered_multiset关联容器也具有同样的用法,并且也会保存其内在的顺序。

另外像std::map, std::multimap, std::unordered_map, std::unordered_multimap,nlohmann也是支持的,但是需要注意的是其中的Key被构造为std::string保存。

std::vector<int> c_vector {1, 2, 3, 4};
json j_vec(c_vector);
// [1, 2, 3, 4]std::deque<double> c_deque {1.2, 2.3, 3.4, 5.6};
json j_deque(c_deque);
// [1.2, 2.3, 3.4, 5.6]std::list<bool> c_list {true, true, false, true};
json j_list(c_list);
// [true, true, false, true]std::forward_list<int64_t> c_flist {12345678909876, 23456789098765, 34567890987654, 45678909876543};
json j_flist(c_flist);
// [12345678909876, 23456789098765, 34567890987654, 45678909876543]std::array<unsigned long, 4> c_array {{1, 2, 3, 4}};
json j_array(c_array);
// [1, 2, 3, 4]std::set<std::string> c_set {"one", "two", "three", "four", "one"};
json j_set(c_set); // only one entry for "one" is used
// ["four", "one", "three", "two"]std::unordered_set<std::string> c_uset {"one", "two", "three", "four", "one"};
json j_uset(c_uset); // only one entry for "one" is used
// maybe ["two", "three", "four", "one"]std::multiset<std::string> c_mset {"one", "two", "one", "four"};
json j_mset(c_mset); // both entries for "one" are used
// maybe ["one", "two", "one", "four"]std::unordered_multiset<std::string> c_umset {"one", "two", "one", "four"};
json j_umset(c_umset); // both entries for "one" are used
// maybe ["one", "two", "one", "four"]std::map<std::string, int> c_map { {"one", 1}, {"two", 2}, {"three", 3} };
json j_map(c_map);
// {"one": 1, "three": 3, "two": 2 }std::unordered_map<const char*, double> c_umap { {"one", 1.2}, {"two", 2.3}, {"three", 3.4} };
json j_umap(c_umap);
// {"one": 1.2, "two": 2.3, "three": 3.4}std::multimap<std::string, bool> c_mmap { {"one", true}, {"two", true}, {"three", false}, {"three", true} };
json j_mmap(c_mmap); // only one entry for key "three" is used
// maybe {"one": true, "two": true, "three": true}std::unordered_multimap<std::string, bool> c_ummap { {"one", true}, {"two", true}, {"three", false}, {"three", true} };
json j_ummap(c_ummap); // only one entry for key "three" is used
// maybe {"one": true, "two": true, "three": true}

当你使用这些通过STL容器构造的json对象时,你只需要安排STL容器那样的方式去使用它。

3.string序列化和反序列化

  • 反序列化:从字节序列恢复JSON对象。
json j = "{ \"happy\": true, \"pi\": 3.141 }"_json;
auto j2 = R"({"happy": true,"pi": 3.141})"_json;
  • 序列化:从JSON对象转化为字节序列。
std::string s = j.dump();    // {"happy":true,"pi":3.141}
std::cout << j.dump(4) << std::endl;//按照如下形式输出
// {
//     "happy": true,
//     "pi": 3.141
// }

dump()返回JSON对象中存储的原始string值。

4.stream的序列化和反序列化

标准输出(std::cout)和标准输入(std::cin)

json j;
std::cin >> j;//从标准输入中反序列化json对象std::cout << j;//将json对象序列化到标准输出中

将json对象序列化到本地文件,或者从存储在本地的文件中反序列化出json对象,是非常常用的一个操作。nlohmann对于这个操作也很简单。

//读取一个json文件,nlohmann会自动解析其中数据
std::ifstream i("file.json");
json j;
i >> j;//以易于查看的方式将json对象写入到本地文件
std::ofstream o("pretty.json");
o << std::setw(4) << j << std::endl;

5. 任意类型转换

我这里总结一下对于任意类型的转换方法。

对于某一种任意数据类型,可以使用如下方式转换:

namespace ns {//首先定义一个结构体struct person {std::string name;std::string address;int age;};
}ns::person p = {"Ned Flanders", "744 Evergreen Terrace", 60};//定义初始化p//从结构体转换到json对象
json j;
j["name"] = p.name;
j["address"] = p.address;
j["age"] = p.age;//从json对象转换到结构体
ns::person p {j["name"].get<std::string>(),j["address"].get<std::string>(),j["age"].get<int>()
};

但是这样的方式在经常需要转换的场景下就不方便了,nlohmann提供了更为方便的方式:

using 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 nsns::person p {"Ned Flanders", "744 Evergreen Terrace", 60};
json j = p;
std::cout << j << std::endl;
// {"address":"744 Evergreen Terrace","age":60,"name":"Ned Flanders"}// conversion: json -> person
auto p2 = j.get<ns::person>();// that's it
assert(p == p2);

只需要在person结构体所在的命名空间下,定义函数to_json()from_json()就可以轻松的完成任意类型到json对象的转换,以及json转换为任意对象。

注意:

  • 1.函数to_json()from_json()和你定义的数据类型在同一个命名空间中;
  • 2.当你要在另外的文件中使用这两个函数时,要正确的包含它所在的头文件;
  • 3.在from_json中要使用at(),因为当你读取不存在的名称时,它会抛出错误。

当可以将任意类型数据方便的转换到json,就可以将这个json方便的序列化到本地保存,也可以从本地反序列化到内存中,比自己去写每一个字节的操作方便多了。

6.建议使用显式类型转换

当从json对象中获取数据时,强烈建议使用显式类型转换。例如:

std::string s1 = "Hello, world!";
json js = s1;
auto s2 = js.get<std::string>();
//不建议
std::string s3 = js;
std::string s4;
s4 = js;// Booleans
bool b1 = true;
json jb = b1;
auto b2 = jb.get<bool>();
//不建议
bool b3 = jb;
bool b4;
b4 = jb;// numbers
int i = 42;
json jn = i;
auto f = jn.get<double>();
//不建议
double f2 = jb;
double f3;
f3 = jb;
// etc.

7.转换JSON到二进制格式

尽管JSON格式非常常用,但是它的缺点也很明显,它并不是一种紧凑的格式,不适合通过网络传输,或者写到本地,常常需要将json对象就行二进制转换,nlohmann库支持多种二进制格式,包括BSON,CBOR,MessagePack和UBJSON.

// create a JSON value
json j = R"({"compact": true, "schema": 0})"_json;// serialize to BSON
std::vector<std::uint8_t> v_bson = json::to_bson(j);// 0x1B, 0x00, 0x00, 0x00, 0x08, 0x63, 0x6F, 0x6D, 0x70, 0x61, 0x63, 0x74, 0x00, 0x01, 0x10, 0x73, 0x63, 0x68, 0x65, 0x6D, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00// roundtrip
json j_from_bson = json::from_bson(v_bson);// serialize to CBOR
std::vector<std::uint8_t> v_cbor = json::to_cbor(j);// 0xA2, 0x67, 0x63, 0x6F, 0x6D, 0x70, 0x61, 0x63, 0x74, 0xF5, 0x66, 0x73, 0x63, 0x68, 0x65, 0x6D, 0x61, 0x00// roundtrip
json j_from_cbor = json::from_cbor(v_cbor);// serialize to MessagePack
std::vector<std::uint8_t> v_msgpack = json::to_msgpack(j);// 0x82, 0xA7, 0x63, 0x6F, 0x6D, 0x70, 0x61, 0x63, 0x74, 0xC3, 0xA6, 0x73, 0x63, 0x68, 0x65, 0x6D, 0x61, 0x00// roundtrip
json j_from_msgpack = json::from_msgpack(v_msgpack);// serialize to UBJSON
std::vector<std::uint8_t> v_ubjson = json::to_ubjson(j);// 0x7B, 0x69, 0x07, 0x63, 0x6F, 0x6D, 0x70, 0x61, 0x63, 0x74, 0x54, 0x69, 0x06, 0x73, 0x63, 0x68, 0x65, 0x6D, 0x61, 0x69, 0x00, 0x7D// roundtrip
json j_from_ubjson = json::from_ubjson(v_ubjson);

如果需要写到本地,可以使用如下方式:

std::ofstream ofs(path, std::ios::out | std::ios::binary);
const auto msgpack = nlohmann::json::to_msgpack(json);
ofs.write(reinterpret_cast<const char*>(msgpack.data()), msgpack.size() * sizeof(uint8_t));
ofs.close();

8.例子

该例子介绍了怎么保存对象中的成员变量到本地,并且从本地读取二进制反序列化对象。

#include <iostream>
#include <nlohmann/json.hpp>
#include <string>
#include <map>
#include <unordered_map>
#include <fstream>using nlohmann::json;
using namespace std;class TEST{
public:TEST(){}void SetValue(){a = 1;b = 2;c = 3;d = 4;e = 5;f = "I love you!";for (int i = 0; i < 10; ++i) {g.emplace_back(i);}for (int i = 0; i < 10; ++i) {h[to_string(i)] = static_cast<double>(i);}for (int i = 0; i < 10; ++i) {json json_temp(i);j[to_string(i)] = json_temp;}}json to_json(){json json_temp;json_temp["a"] = a;json_temp["b"] = b;json_temp["c"] = c;json_temp["d"] = d;json_temp["e"] = e;json_temp["f"] = f;json_temp["g"] = g;json_temp["h"] = h;json_temp["j"] = j;return json_temp;}void from_json(const json& json_temp){a = json_temp.at("a").get<int>();b = json_temp.at("b").get<float>();c = json_temp.at("c").get<double>();d = json_temp.at("d").get<long>();e = json_temp.at("e").get<long long>();f = json_temp.at("f").get<string>();g = json_temp.at("g").get<vector<int>>();h = json_temp.at("h").get<map<string, double>>();j = json_temp.at("j").get<unordered_map<string, json>>();}public:int a;float b;double c;long d;long long e;string f;vector<int> g;map<string, double> h;unordered_map<string, json> j;
};int main(){TEST test;test.SetValue();json js = test.to_json();const vector<uint8_t> message_pack = nlohmann::json::to_msgpack(js);ofstream ofs("./binary", std::ios::out | std::ios::binary | std::ios::trunc);ofs.write(reinterpret_cast<const char*>(message_pack.data()), message_pack.size()*sizeof(uint8_t));ofs.close();ifstream ifs("./binary", ios::binary | ios::in);std::vector<uint8_t> message_pack_1;while (true){uint8_t buffer;ifs.read(reinterpret_cast<char*>(&buffer), sizeof(uint8_t));if (ifs.eof()){break;}message_pack_1.emplace_back(buffer);}ifs.close();json js1 = nlohmann::json::from_msgpack(message_pack_1);TEST test1;test1.from_json(js1);cout << test1.a << endl;cout << test1.b << endl;cout << test1.c << endl;cout << test1.d << endl;cout << test1.e << endl;cout << test1.f << endl;return 0;
}

温馨提示:虽然将数据转换为json对象,然后进行保存和序列化会很方便,但是当数据类巨大时,达到百万或者千万的级别时,再使用这样的方式,在构造json对象时会消耗很多时间,我试过一个大概300M的数据,转化为json对象时大概需要几十秒。而300M兆的数据写到本地时只需要0.1秒。所以此时最好还是直接对原始的数据进行操作写入本地,就别再转化到json对象中了,虽然算法会麻烦一些,但是获得的速度是非常快的。

参考资料:

  1. nlohmann/json

这篇关于json库nlohmann简单使用教程,快速入手,完成json对象的构建,从STL构造json,以及序列化和反序列化操作,二进制写入、读取本地数据的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1033706

相关文章

C语言中联合体union的使用

本文编辑整理自: http://bbs.chinaunix.net/forum.php?mod=viewthread&tid=179471 一、前言 “联合体”(union)与“结构体”(struct)有一些相似之处。但两者有本质上的不同。在结构体中,各成员有各自的内存空间, 一个结构变量的总长度是各成员长度之和。而在“联合”中,各成员共享一段内存空间, 一个联合变量

C++对象布局及多态实现探索之内存布局(整理的很多链接)

本文通过观察对象的内存布局,跟踪函数调用的汇编代码。分析了C++对象内存的布局情况,虚函数的执行方式,以及虚继承,等等 文章链接:http://dev.yesky.com/254/2191254.shtml      论C/C++函数间动态内存的传递 (2005-07-30)   当你涉及到C/C++的核心编程的时候,你会无止境地与内存管理打交道。 文章链接:http://dev.yesky

Spring Cloud:构建分布式系统的利器

引言 在当今的云计算和微服务架构时代,构建高效、可靠的分布式系统成为软件开发的重要任务。Spring Cloud 提供了一套完整的解决方案,帮助开发者快速构建分布式系统中的一些常见模式(例如配置管理、服务发现、断路器等)。本文将探讨 Spring Cloud 的定义、核心组件、应用场景以及未来的发展趋势。 什么是 Spring Cloud Spring Cloud 是一个基于 Spring

乐鑫 Matter 技术体验日|快速落地 Matter 产品,引领智能家居生态新发展

随着 Matter 协议的推广和普及,智能家居行业正迎来新的发展机遇,众多厂商纷纷投身于 Matter 产品的研发与验证。然而,开发者普遍面临技术门槛高、认证流程繁琐、生产管理复杂等诸多挑战。  乐鑫信息科技 (688018.SH) 凭借深厚的研发实力与行业洞察力,推出了全面的 Matter 解决方案,包含基于乐鑫 SoC 的 Matter 硬件平台、基于开源 ESP-Matter SDK 的一

Tolua使用笔记(上)

目录   1.准备工作 2.运行例子 01.HelloWorld:在C#中,创建和销毁Lua虚拟机 和 简单调用。 02.ScriptsFromFile:在C#中,对一个lua文件的执行调用 03.CallLuaFunction:在C#中,对lua函数的操作 04.AccessingLuaVariables:在C#中,对lua变量的操作 05.LuaCoroutine:在Lua中,

RedHat运维-Linux文本操作基础-AWK进阶

你不用整理,跟着敲一遍,有个印象,然后把它保存到本地,以后要用再去看,如果有了新东西,你自个再添加。这是我参考牛客上的shell编程专项题,只不过换成了问答的方式而已。不用背,就算是我自己亲自敲,我现在好多也记不住。 1. 输出nowcoder.txt文件第5行的内容 2. 输出nowcoder.txt文件第6行的内容 3. 输出nowcoder.txt文件第7行的内容 4. 输出nowcode

一份LLM资源清单围观技术大佬的日常;手把手教你在美国搭建「百万卡」AI数据中心;为啥大模型做不好简单的数学计算? | ShowMeAI日报

👀日报&周刊合集 | 🎡ShowMeAI官网 | 🧡 点赞关注评论拜托啦! 1. 为啥大模型做不好简单的数学计算?从大模型高考数学成绩不及格说起 司南评测体系 OpenCompass 选取 7 个大模型 (6 个开源模型+ GPT-4o),组织参与了 2024 年高考「新课标I卷」的语文、数学、英语考试,然后由经验丰富的判卷老师评判得分。 结果如上图所

二进制文件转化成文本文件

文章中如果有写错、表述不明、有疑问或者需要扩展的知识,欢迎留言或者私信~   1.区别 如果一个文件说是文本文件,使用任何一种文本编辑器打开可以展现出人类可读信息字符,因为编码都符合某种编码方式,如ASCII、UTF8、GB2312等等(在文件头可以读出来是什么编码方式,然后文本编辑器再按照规则去读取翻译成对应的字符,展示给我们的就是可读的了)。(关于编码方式不了解可以看这一篇) 如果一

Vim使用基础篇

本文内容大部分来自 vimtutor,自带的教程的总结。在终端输入vimtutor 即可进入教程。 先总结一下,然后再分别介绍正常模式,插入模式,和可视模式三种模式下的命令。 目录 看完以后的汇总 1.正常模式(Normal模式) 1.移动光标 2.删除 3.【:】输入符 4.撤销 5.替换 6.重复命令【. ; ,】 7.复制粘贴 8.缩进 2.插入模式 INSERT

JAVA读取MongoDB中的二进制图片并显示在页面上

1:Jsp页面: <td><img src="${ctx}/mongoImg/show"></td> 2:xml配置: <?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001