JSON(JavaScript Object Notation)是一种非常理想的轻量级数据交换格式,基于以下两种结构:

  • 名-值对的集合 - 在各种语言中,被实现为对象、记录、结构、字典、散列表、键列表、关联数组等;
  • 值的有序列表 - 在大多数语言中,被实现为数组、向量、列表、序列等。

JSON for Modern C++ 的设计目标就是:

  • 直观的语法 - 使用现代 C++ 操作符魔力,使 JSON 成为第一类数据类型
  • 微型的集成 - 仅有 json.hpp,无依赖、无复杂构建系统,符合 C++11 标准
  • 严格的测试 - 单元测试 100% 覆盖,包括所有的异常行为
  • 高效的内存
  • 更快的速度

集成 JSON for Modern C++ 非常简单,仅需 json.hpp(GitHub)源文件即可:

#include "json.hpp"
// 为方便起见,设置命名空间
using json = nlohmann::json;

对于 GCC 和 Clang 编译器来说,别忘了打开 C++11 支持,例如 -std=c++11

下面我们通过一些示例来学习 JSON for Modern C++ 的使用方法。

JSON 作为第一类数据类型

直接上栗子:

// 创建一个空结构(null)
json j;// 添加一个数值,存为 double
j["pi"] = 3.141;
// 添加一个布尔值,存为 bool
j["happy"] = true;
// 添加一个字符串,存为 std::string
j["name"] = "Niels";
// 添加另一个空对象,使用 nullptr
j["nothing"] = nullptr;
// 添加一个对象内的对象
j["answer"]["everything"] = 42;
// 添加一个数组,存为 std::vector
j["list"] = {1, 0, 2};
// 添加另一个对象
j["object"] = {{"currency", "USD"}, {"value", 42.99}};

当然,还可以直接这样写:

json j2 = {{"pi", 3.141},{"happy", true},{"name", "Niels"},{"nothing", nullptr},{"answer", {{"everything", 42}}},{"list", {1, 0, 2}},{"object", {{"currency", "USD"},{"value", 42.99}}}
};

序列化和反序列化

序列化至字符串 / 反序列化自字符串

通过为字符串字面量附加 _json 可以创建一个 JSON 对象(反序列化):

// 从字符串字面量创建对象
json j = "{\"happy\": true, \"pi\": 3.141}"_json;// 最好是使用原始字符串字面量
auto j2 = R"({"happy": true, "pi": 3.141})"_json;// 显式地分析
auto j3 = json::parse("{\"happy\": true, \"pi\": 3.141}");

还可以获取一个字符串表示形式(序列化):

// 显式地转换至字符串
std::string s = j.dump()// 传入缩进空格数,使用良好的打印形式进行序列化
std::cout << j.dump(4) << std::endl;

.dump() 返回的是序列化的值,而 .get<std::string>() 返回的是原始字符串值。

序列化至流 / 反序列化自流

流包括文件、字符串流等,例如:

// 反序列化自标准输入
json j;
std::cin >> j;// 序列化至标准输出
std::cout << j;// 设置良好打印形式所需的缩进
std::cout << std::setw(4) << j << std::endl;

序列化至文件,或是反序列化自文件,例如:

// 读入一个 JSON 文件
std::ifstream i("file.json");
json j;
i >> j;// 将美化的 JSON 写入另一个文件
std::ofstream o("pretty.json");
o << std::setw(4) << j << std:endl;

读自迭代器范围

可以从一个迭代器范围中读取 JSON,迭代器的内容包含连续的字节序列,例如 std::vector<std::uint8_t>

std::vector<std::uint8_t> v = {'t', 'r', 'u', 'e'};json j = json::parse(v.begin(), v.end());
// 或
json j = json::parse(v);

像 STL 容器一样访问

JSON 类的行为就像 STL 容器一般,满足 ReversibleContainer(可逆容器)的要求,例如:

// 使用 push_back 创建一个数组
json j;
j.push_back("foo");
j.push_back(1);
j.push_back(true);
// 也可以使用 emplace_back
j.emplace_back(1.78);// 在数组上进行迭代
for (json::iterator it = j.begin(); it != j.end(); ++it) {std::cout << *it << '\n';
}// 基于范围的 for 循环
for (auto& element : j) {std::cout << element << '\n';
}// 访问器 getter/setter
const std::string tmp = j[0];
j[1] = 42;
bool foo = j.at(2);// 比较
j == "[\"foo\", 1, true]"_json;  // true// 其他东东
j.size();     // 3 条
j.empty();    // false
j.type();     // json::value_t::array
j.clear();    // 数组再次为空// 便利的类型检测器
j.is_null();
j.is_boolean();
j.is_number();
j.is_object();
j.is_array();
j.is_string();// 创建一个 JSON 对象
json o;
o["foo"] = 23;
o["bar"] = false;
o["baz"] = 3.141;// 也可以使用 emplace
o.emplace("weather", "sunny");// 为对象指定迭代成员函数
for (json::iterator it = o.begin(); it != o.end(); ++it) {std::cout << it.key() << " : " << it.value() << "\n";
}// 查找一个条目
if (o.find("foo") != o.end()) {// 找到键为 "foo" 的一个条目
}// 使用计数 count()
int foo_present = o.count("foo");  // 1
int fob_present = o.count("fob");  // 0// 删除条目
o.erase("foo");

从 STL 容器转换至 JSON

任何序列化容器,包括 std::arraystd::vectorstd::dequestd::forward_liststd::list,都可以用来构建 JSON 数组。同样地,关联化容器,包括 std::setstd::multisetstd::unordered_setstd::unordered_multiset,也可以用来构建 JSON 数组。不同的是,数组中元素的顺序是否依赖于 STL 容器中元素的顺序。

std::vector<int> c_vector {1, 2, 3, 4};
json j_vec(c_vector);std::deque<double> c_deque {1.2, 2.3, 3.4, 5.6};
json j_deque(c_deque);std::list<bool> c_list {true, true, false, true};
json j_list(c_list);std::forward_list<int64_t> c_flist {12345678909876, 23456789098765, 34567890987654, 45678909876543};
json j_flist(c_flist);std::array<unsigned long, 4> c_array {{1, 2, 3, 4}};
json j_array(c_array);std::set<std::string> c_set {"one", "two", "three", "four", "one"};
json j_set(c_set);std::unordered_set<std::string> c_uset {"one", "two", "three", "four", "one"};
json j_uset(c_uset);std::multiset<std::string> c_mset {"one", "two", "one", "four"};
json j_mset(c_mset);std::unordered_multiset<std::string> c_umset {"one", "two", "one", "four"};
json j_umset(c_umset);

同样地,任何关联的键-值容器,包括 std::mapstd::multimapstd::unordered_mapstd::unordered_multimap, 都可以用来创建 JSON 对象。

std::map<std::string, int> c_map {{"one", 1}, {"two", 2}, {"three", 3}};
json j_map(c_map);std::unordered_map<const char*, double> c_umap {{"one", 1.2}, {"two", 2.3}, {"three", 3.4}};
json j_umap(c_umap);std::multimap<std::string, bool> c_mmap {{"one", true}, {"two", true}, {"three", false}, {"three", true}};
json j_mmap(c_mmap); std::unordered_multimap<std::string, bool> c_ummap {{"one", true}, {"two", true}, {"three", false}, {"three", true}};
json j_ummap(c_ummap); 

JSON Pointer 和 JSON Patch

JSON for Modern C++ 库支持 JSON Pointer(RFC 6901) 和 JSON Patch(RFC 6902),分别用于定位结构化数值,或描述两个 JSON 之间的不同。

// 一个 JSON 值
json j_original = R"({"baz":["one", "two", "three"], "foo": "bar"})"_json;// 使用 JSON Pointer 访问其成员
j_original["/baz/1"_json_pointer];  // => "two"// 一个 JSON Patch
json j_patch = R"([{"op": "replace", "path": "/baz", "value": "boo"}, {"op": "add", "path": "/hello", "value": ["world"]}, {"op": "remove", "path": "/foo"}])"_json;// 为 JSON 打补丁
json j_result = j_original.patch(j_patch);// 计算两个 JSON 值之间的差异
json::diff(j_result, j_original);

隐式转换

JSON 对象的类型是由表达式所决定的,所存储的值隐式地进行转换。例如:

// 字符串
std::string s1 = "Hello, world!";
json js = s1;
std::string s2 = js;// 布尔值
bool b1 = true;
json jb = b1;
bool b2 = jb;// 数值
int i = 42;
json jn = i;
double f = jn;

当然,也可以显式地请求数值:

std::string vs = js.get<std::string>();
bool vb = jb.get<bool>();
int vi = jn.get<int>();

任意类型转换

不仅仅是 STL 容器和标量类型,所有类型都可以序列化至 JSON。例如,下面是一个 ns::person 结构:

namespace ns {// 一个简单的结构,为 person 建模struct person {std::string name;std::string address;int age;};
}

我们来看看自定义结构和 JSON 之间是如何转换的:

ns::person p = {"Ned Flanders", "744 Evergreen Terrace", 60};// 转换至 JSON:将每个值拷贝至 JSON 对象
json j;
j["name"] = p.name;
j["address"] = p.address;
j["age"] = p.age;// 转换自 JSON:从 JSON 对象中拷贝每个值
ns::person p {j["name"].get<std::string>(),j["address"].get<std::string>(),j["age"].get<int>()
};

以上可以工作,但使用了太多的样板。幸运的是,还可以有更好的方法:

// 创建 person 结构
ns::person p {"Ned Flanders", "744 Evergreen Terrace", 60};// 转换:person → json
json j = p;std::cout << j << std::endl;// 转换:json → person
ns::person p2 = j;

要使以上代码工作,仅需提供两个函数:

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) {p.name = j.at("name").get<std::string>();p.name = j.at("address").get<std::string>();p.name = j.at("age").get<int>();}
}

二进制格式(CBOR 和 MessagePack)

虽然 JSON 数据格式似乎是无所不在的,但却不怎么适用于网络数据交换。因此,提供 CBOR(Concise Binary Object Representation)和 MessagePack 功能,使得可以将 JSON 数据编码成字节向量,或是将字节向量解码成 JSON 数据。

// 创建一个 JSON 数据
json j = R"({"compact": true, "schema": 0})"_json;// 序列化至 CBOR
std::vector<std::uint8_t> v_cbor = json::to_cbor(j);
// v_cbor => 0xa2, 0x67, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0xf5, 0x66, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x00// 从 CBOR 中返回 JSON
json j_from_cbor = json::from_cbor(v_cbor);// 序列化至 MessagePack
std::vector<std::uint8_t> v_msgpack = json::to_msgpack(j);
// v_msgpack => 0x82, 0xa7, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0xc3, 0xa6, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x00// 从 MessagePack 中返回 JSON
json j_from_msgpack = json::from_msgpack(v_msgpack);

JSON for Modern C++相关推荐

  1. JSON for Modern C++ 3.6.0 发布

    开发四年只会写业务代码,分布式高并发都不会还做程序员? >>>   JSON for Modern C++ 3.6.0 发布了,此版本为 JSON 指针添加了一些便利功能,引入了一个 ...

  2. json 序列化json for modern c++

    使用的是json三方库json for modern c++ 先使用json定义一个对象,可以想象成STL容器,中间储存的一个个的键值对 #include <cstdio> #includ ...

  3. [json] JSON for Modern C++

    有幸能接触到这个,这是我遇到的使用最方便的json了,效率没研究过! 简单了使用了下,感觉非常好用,记录下: 要使用这个json,只需要使用json.hpp就行,放入自己的工程里,但是我这里是安装过的 ...

  4. Json文件解析(上)

    Json文件解析(上) 代码地址:https://github.com/nlohmann/json 自述文件 alt=GitHub赞助商 data-canonical-src="https: ...

  5. 一个好用的C++的json库

    此文首发于我的个人博客:一个好用的C++的json库 - zhang0peter的个人博客 今天在找可以在C++中用的JSON库. 一个比较好用的传统的JSON库是JsonCpp, GitHub地址: ...

  6. 【转】ABAP内表数据和JSON格式互转

    本程序演示ABAP内表数据如何转为JSON格式, 以及JSON数据如何放入内表. REPORT ZTEST005. DATA : JSON_SER TYPE REF TO CL_TREX_JSON_S ...

  7. C++使用JSON的序列化与反序列化

    这里使用的json解析工具为JSON for Modern C++,使用的话仅需要包含头文件. 获取方式:wget https://github.com/nlohmann/json/releases/ ...

  8. C++ JSON库:JSON for Morden C++

    最近因为项目的需要,需要对JSON进行一定的数据处理,因为想要用C++进行编码,便对C++的JSON库进行的调研,发现这个库比较好用:JSON for Morder C++. 看到一个大神对这个库实现 ...

  9. java multimap 序列化_C++ JSON库的使用

    1. 如何使用? 2. 常用方法 2.1 创建json对象 2.1.1 使用cin,cout输入输出流 2.1.2 提供根据键直接生成键值对的方法 2.1.3 json::array json::ob ...

  10. 【C++】C++库nlohmann / json的使用

    nlohmann / json for Modern C++ 前言 一.nlohmann/json库简述 1. 概述 2. 优点 3. 配置 二.nlohmann/json库的基本操作 1. 读取 / ...

最新文章

  1. sfdisk命令的使用技巧
  2. 类和对象—继承—同名成员处理
  3. Oralcle存储过程书写规范
  4. 初学者可能不知道的vue技巧
  5. IPC$连接常见问答
  6. php表白情话,向一个人表白 抖音最火99句情话告白
  7. 嵌入式开发环境ARM+linux的搭建
  8. 设计模式-UML关系基础
  9. 【交往智慧】005.做一个愿意聆听的人
  10. “我要彻底放弃 Debian 操作系统!”
  11. web系统权限之数据权限
  12. 求最大和 java_用java求最大子段和
  13. 五大地形等高线特征_等高线地形图的判读技巧
  14. Jenkins 打包项目出错汇总(持续)
  15. 怎么把线稿提取出来_如何提取线稿为你所用?不会的来!
  16. 【数据分析】贝叶斯原理
  17. Win10突然就不能连接网络了怎么办
  18. Google 黑客搜索技巧
  19. 如何高效编写Go单元测试(一)
  20. 网络安全工程师入门教程(非常详细)从零基础入门到精通,看完这一篇就够了

热门文章

  1. 同时打开多个独立Excel窗口
  2. 安装错误 Package requirements (json-c) were not met: No package ‘json-c‘ found
  3. mysql局域网访问6_mysql局域网访问
  4. java 对象 转为繁体,java调用opencc,将简体中文转换成繁体
  5. Unity 自动化构建方案:一键实现版本管理与打包、压缩
  6. aws服务器如何修改远程密码,谷歌云、亚马逊 AWS 设置 root 密码并开启 SSH 登录...
  7. emulator-5554 offline问题
  8. 阿里20亿美金收购考拉,丁磊到底是怎么“失身”的?
  9. python 验证码识别
  10. 计算机笔记检讨,上课没做笔记的反省检讨书