JSON for Modern C++
来源:互联网 发布:钢结构螺栓验算软件 编辑:程序博客网 时间:2024/06/05 09:40
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;// 添加一个数值,存为 doublej["pi"] = 3.141;// 添加一个布尔值,存为 bool j["happy"] = true;// 添加一个字符串,存为 std::stringj["name"] = "Niels";// 添加另一个空对象,使用 nullptrj["nothing"] = nullptr;// 添加一个对象内的对象j["answer"]["everything"] = 42;// 添加一个数组,存为 std::vectorj["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_backj.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/setterconst std::string tmp = j[0];j[1] = 42;bool foo = j.at(2);// 比较j == "[\"foo\", 1, true]"_json; // true// 其他东东j.size(); // 3 条j.empty(); // falsej.type(); // json::value_t::arrayj.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;// 也可以使用 emplaceo.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"); // 1int fob_present = o.count("fob"); // 0// 删除条目o.erase("foo");
从 STL 容器转换至 JSON
任何序列化容器,包括 std::array
, std::vector
, std::deque
, std::forward_list
, std::list
,都可以用来构建 JSON 数组。同样地,关联化容器,包括 std::set
, std::multiset
, std::unordered_set
, std::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::map
, std::multimap
, std::unordered_map
, std::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 Patchjson 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 → jsonjson j = p;std::cout << j << std::endl;// 转换:json → personns::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;// 序列化至 CBORstd::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 中返回 JSONjson j_from_cbor = json::from_cbor(v_cbor);// 序列化至 MessagePackstd::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 中返回 JSONjson j_from_msgpack = json::from_msgpack(v_msgpack);
- JSON for Modern C++
- JSON for Modern C++ 玩具
- json for modern c++的使用
- Support For C++11/14/17 Features (Modern C++)
- Modern C++(二)Range-based for loop(基于范围的for循环)
- Modern Objective-C
- Modern Objective-C
- Modern Objective-C
- Adopting Modern Objective-C
- Adopting Modern Objective-C
- Adopting Modern Objective-C
- 《Effective Modern C++》读书笔记
- Effective Modern C++(笔记)
- Character Encodings For Modern Programmers
- Modern Methods for Sentiment Analysis
- 初识Modern UI for WPF
- <Effective Modern C++>Item 18: Use std::unique_ptr for exclusive-ownership resource management.
- modern c 8.1 编程题
- Angular2权威教程.pdf 免费下载
- echarts pie
- Secret 的使用场景
- Spring MVC-循序渐进之Model 2和MVC(更新中)
- [2017集训队作业自选题#107]An unavoidable detour for home
- JSON for Modern C++
- 【给詹詹的Python学习笔记四】
- hdata datax交流总结
- Domain-Driven+Design+Tackling+Complexity+in+the+Heart+of+Software.pdf 英文原版 免费下载
- 513. Find Bottom Left Tree Value(C++)
- 1076: [SCOI2008]奖励关
- 【扩展欧拉定理-降幂大法】Balkan OI 2016[数塔]题解
- Linux crontab 命令详细用法及示例
- 并发解决办法