JSON for Modern C++
JSON(JavaScript Object Notation)是一種非常理想的輕量級(jí)數(shù)據(jù)交換格式,基于以下兩種結(jié)構(gòu):
- 名-值對(duì)的集合 - 在各種語言中,被實(shí)現(xiàn)為對(duì)象、記錄、結(jié)構(gòu)、字典、散列表、鍵列表、關(guān)聯(lián)數(shù)組等;
- 值的有序列表 - 在大多數(shù)語言中,被實(shí)現(xiàn)為數(shù)組、向量、列表、序列等。
JSON for Modern C++ 的設(shè)計(jì)目標(biāo)就是:
- 直觀的語法 - 使用現(xiàn)代 C++ 操作符魔力,使 JSON 成為第一類數(shù)據(jù)類型
- 微型的集成 - 僅有 json.hpp,無依賴、無復(fù)雜構(gòu)建系統(tǒng),符合 C++11 標(biāo)準(zhǔn)
- 嚴(yán)格的測(cè)試 - 單元測(cè)試 100% 覆蓋,包括所有的異常行為
- 高效的內(nèi)存
- 更快的速度
集成 JSON for Modern C++ 非常簡(jiǎn)單,僅需 json.hpp(GitHub)源文件即可:
#include "json.hpp" // 為方便起見,設(shè)置命名空間 using json = nlohmann::json;對(duì)于 GCC 和 Clang 編譯器來說,別忘了打開 C++11 支持,例如 -std=c++11。
下面我們通過一些示例來學(xué)習(xí) JSON for Modern C++ 的使用方法。
JSON 作為第一類數(shù)據(jù)類型
直接上栗子:
// 創(chuàng)建一個(gè)空結(jié)構(gòu)(null) json j;// 添加一個(gè)數(shù)值,存為 double j["pi"] = 3.141; // 添加一個(gè)布爾值,存為 bool j["happy"] = true; // 添加一個(gè)字符串,存為 std::string j["name"] = "Niels"; // 添加另一個(gè)空對(duì)象,使用 nullptr j["nothing"] = nullptr; // 添加一個(gè)對(duì)象內(nèi)的對(duì)象 j["answer"]["everything"] = 42; // 添加一個(gè)數(shù)組,存為 std::vector j["list"] = {1, 0, 2}; // 添加另一個(gè)對(duì)象 j["object"] = {{"currency", "USD"}, {"value", 42.99}};當(dāng)然,還可以直接這樣寫:
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 可以創(chuàng)建一個(gè) JSON 對(duì)象(反序列化):
// 從字符串字面量創(chuàng)建對(duì)象 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}");還可以獲取一個(gè)字符串表示形式(序列化):
// 顯式地轉(zhuǎn)換至字符串 std::string s = j.dump()// 傳入縮進(jìn)空格數(shù),使用良好的打印形式進(jìn)行序列化 std::cout << j.dump(4) << std::endl;.dump() 返回的是序列化的值,而 .get<std::string>() 返回的是原始字符串值。
序列化至流 / 反序列化自流
流包括文件、字符串流等,例如:
// 反序列化自標(biāo)準(zhǔn)輸入 json j; std::cin >> j;// 序列化至標(biāo)準(zhǔn)輸出 std::cout << j;// 設(shè)置良好打印形式所需的縮進(jìn) std::cout << std::setw(4) << j << std::endl;序列化至文件,或是反序列化自文件,例如:
// 讀入一個(gè) JSON 文件 std::ifstream i("file.json"); json j; i >> j;// 將美化的 JSON 寫入另一個(gè)文件 std::ofstream o("pretty.json"); o << std::setw(4) << j << std:endl;讀自迭代器范圍
可以從一個(gè)迭代器范圍中讀取 JSON,迭代器的內(nèi)容包含連續(xù)的字節(jié)序列,例如 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 創(chuàng)建一個(gè)數(shù)組 json j; j.push_back("foo"); j.push_back(1); j.push_back(true); // 也可以使用 emplace_back j.emplace_back(1.78);// 在數(shù)組上進(jìn)行迭代 for (json::iterator it = j.begin(); it != j.end(); ++it) {std::cout << *it << '\n'; }// 基于范圍的 for 循環(huán) 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(); // 數(shù)組再次為空// 便利的類型檢測(cè)器 j.is_null(); j.is_boolean(); j.is_number(); j.is_object(); j.is_array(); j.is_string();// 創(chuàng)建一個(gè) JSON 對(duì)象 json o; o["foo"] = 23; o["bar"] = false; o["baz"] = 3.141;// 也可以使用 emplace o.emplace("weather", "sunny");// 為對(duì)象指定迭代成員函數(shù) for (json::iterator it = o.begin(); it != o.end(); ++it) {std::cout << it.key() << " : " << it.value() << "\n"; }// 查找一個(gè)條目 if (o.find("foo") != o.end()) {// 找到鍵為 "foo" 的一個(gè)條目 }// 使用計(jì)數(shù) count() int foo_present = o.count("foo"); // 1 int fob_present = o.count("fob"); // 0// 刪除條目 o.erase("foo");從 STL 容器轉(zhuǎn)換至 JSON
任何序列化容器,包括 std::array, std::vector, std::deque, std::forward_list, std::list,都可以用來構(gòu)建 JSON 數(shù)組。同樣地,關(guān)聯(lián)化容器,包括 std::set, std::multiset, std::unordered_set, std::unordered_multiset,也可以用來構(gòu)建 JSON 數(shù)組。不同的是,數(shù)組中元素的順序是否依賴于 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);同樣地,任何關(guān)聯(lián)的鍵-值容器,包括 std::map, std::multimap, std::unordered_map, std::unordered_multimap, 都可以用來創(chuàng)建 JSON 對(duì)象。
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++ 庫(kù)支持 JSON Pointer(RFC 6901) 和 JSON Patch(RFC 6902),分別用于定位結(jié)構(gòu)化數(shù)值,或描述兩個(gè) JSON 之間的不同。
// 一個(gè) JSON 值 json j_original = R"({"baz":["one", "two", "three"], "foo": "bar"})"_json;// 使用 JSON Pointer 訪問其成員 j_original["/baz/1"_json_pointer]; // => "two"// 一個(gè) JSON Patch json j_patch = R"([{"op": "replace", "path": "/baz", "value": "boo"}, {"op": "add", "path": "/hello", "value": ["world"]}, {"op": "remove", "path": "/foo"}])"_json;// 為 JSON 打補(bǔ)丁 json j_result = j_original.patch(j_patch);// 計(jì)算兩個(gè) JSON 值之間的差異 json::diff(j_result, j_original);隱式轉(zhuǎn)換
JSON 對(duì)象的類型是由表達(dá)式所決定的,所存儲(chǔ)的值隱式地進(jìn)行轉(zhuǎn)換。例如:
// 字符串 std::string s1 = "Hello, world!"; json js = s1; std::string s2 = js;// 布爾值 bool b1 = true; json jb = b1; bool b2 = jb;// 數(shù)值 int i = 42; json jn = i; double f = jn;當(dāng)然,也可以顯式地請(qǐng)求數(shù)值:
std::string vs = js.get<std::string>(); bool vb = jb.get<bool>(); int vi = jn.get<int>();任意類型轉(zhuǎn)換
不僅僅是 STL 容器和標(biāo)量類型,所有類型都可以序列化至 JSON。例如,下面是一個(gè) ns::person 結(jié)構(gòu):
namespace ns {// 一個(gè)簡(jiǎn)單的結(jié)構(gòu),為 person 建模struct person {std::string name;std::string address;int age;}; }我們來看看自定義結(jié)構(gòu)和 JSON 之間是如何轉(zhuǎn)換的:
ns::person p = {"Ned Flanders", "744 Evergreen Terrace", 60};// 轉(zhuǎn)換至 JSON:將每個(gè)值拷貝至 JSON 對(duì)象 json j; j["name"] = p.name; j["address"] = p.address; j["age"] = p.age;// 轉(zhuǎn)換自 JSON:從 JSON 對(duì)象中拷貝每個(gè)值 ns::person p {j["name"].get<std::string>(),j["address"].get<std::string>(),j["age"].get<int>() };以上可以工作,但使用了太多的樣板。幸運(yùn)的是,還可以有更好的方法:
// 創(chuàng)建 person 結(jié)構(gòu) ns::person p {"Ned Flanders", "744 Evergreen Terrace", 60};// 轉(zhuǎn)換:person → json json j = p;std::cout << j << std::endl;// 轉(zhuǎn)換:json → person ns::person p2 = j;要使以上代碼工作,僅需提供兩個(gè)函數(shù):
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>();} }二進(jìn)制格式(CBOR 和 MessagePack)
雖然 JSON 數(shù)據(jù)格式似乎是無所不在的,但卻不怎么適用于網(wǎng)絡(luò)數(shù)據(jù)交換。因此,提供 CBOR(Concise Binary Object Representation)和 MessagePack 功能,使得可以將 JSON 數(shù)據(jù)編碼成字節(jié)向量,或是將字節(jié)向量解碼成 JSON 數(shù)據(jù)。
// 創(chuàng)建一個(gè) JSON 數(shù)據(jù) 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);總結(jié)
以上是生活随笔為你收集整理的JSON for Modern C++的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 纽约大学计算机工程专业课程,NYU的El
- 下一篇: 远心镜头原理