JSON for Modern C++


轉(zhuǎn)到github

nlohmann::json在項(xiàng)目中的實(shí)際使用詳見(jiàn)C++靜態(tài)代碼檢測(cè)工具:cppdetector

設(shè)計(jì)目標(biāo)

已經(jīng)有無(wú)數(shù)的JSON庫(kù)该镣,每個(gè)庫(kù)都有其存在的理由筋帖。我們有這些設(shè)計(jì)目標(biāo):

  • 直觀的語(yǔ)法。在像Python這樣的語(yǔ)言中究恤,JSON感覺(jué)就像是一級(jí)數(shù)據(jù)類(lèi)型拜姿。我們使用現(xiàn)代C++的操作魔法來(lái)實(shí)現(xiàn)類(lèi)似普通代碼般的感覺(jué)憾儒∈铺埽看看下面的例子苗傅,你就會(huì)明白我的意思。

  • 整合缸浦。我們的整個(gè)代碼只有一個(gè)頭文件json.hpp夕冲。沒(méi)有庫(kù),沒(méi)有子項(xiàng)目裂逐,沒(méi)有依賴項(xiàng)歹鱼,沒(méi)有復(fù)雜的構(gòu)建系統(tǒng),僅僅使用標(biāo)準(zhǔn)C ++ 11編寫(xiě)卜高∶忠觯總而言之南片,不需要調(diào)整任何編譯器標(biāo)志或項(xiàng)目設(shè)置。

  • 壓力測(cè)試庭敦。我們的類(lèi)經(jīng)過(guò)嚴(yán)格的單元測(cè)試疼进,涵蓋了100%的代碼,包括所有特殊行為秧廉。此外伞广,我們使用ValgrindClang Sanitizers做了檢測(cè),沒(méi)有內(nèi)存泄漏疼电。還使用谷歌OSS-Fuzz對(duì)所有解析器進(jìn)行24/7模糊測(cè)試嚼锄,到目前為止有效地執(zhí)行了數(shù)十億次測(cè)試。為了保持高質(zhì)量蔽豺,該項(xiàng)目遵循核心基礎(chǔ)設(shè)施倡議(CII)的最佳實(shí)踐区丑。

對(duì)我們來(lái)說(shuō)并不那么重要的其他方面:

  • 內(nèi)存效率。每個(gè)JSON對(duì)象都有一個(gè)指針(聯(lián)合的最大大忻K洹)和一個(gè)枚舉元素(1個(gè)字節(jié))的開(kāi)銷(xiāo)刊苍。默認(rèn)泛化使用以下C ++數(shù)據(jù)類(lèi)型:std::string用于字符串,int64_t濒析,uint64_tdouble用于數(shù)字正什,std::map用于對(duì)象,std::vector用于數(shù)組和bool用于布爾值号杏。但是婴氮,您可以根據(jù)需要特化basic_json通用類(lèi)。

  • 速度盾致≈骶肯定有更快的JSON庫(kù)。但是庭惜,如果您的目標(biāo)是通過(guò)添加單個(gè)頭文件添加JSON支持來(lái)加速開(kāi)發(fā)罩驻,那么這個(gè)庫(kù)就是您的選擇。如果您知道如何使用std::vectorstd::map护赊,你就已經(jīng)準(zhǔn)備好了惠遏。

有關(guān)詳細(xì)信息,請(qǐng)參閱貢獻(xiàn)指南骏啰。

  • 整合
    json.hpp唯一所需的文件节吮,在目錄single_include/nlohmann這里。你需要添加:
#include <nlohmann/json.hpp>

// for convenience
using json = nlohmann::json;

到您要處理JSON的文件時(shí)判耕,設(shè)置必要的開(kāi)關(guān)以啟用C ++ 11(例如透绩,-std=c++11對(duì)于GCC和Clang)。

您可以進(jìn)一步使用文件include/nlohmann/json_fwd.hpp進(jìn)行前向聲明。安裝json_fwd.hpp(作為cmake安裝步驟的一部分)帚豪,可以通過(guò)設(shè)置-DJSON_MultipleHeaders=ON來(lái)實(shí)現(xiàn)碳竟。

包管理器

??如果您使用的是OS X和Homebrew,只需鍵入brew tap nlohmann/jsonbrew install nlohmann_json設(shè)置即可狸臣。如果您想要最新版本而不是最新版本瞭亮,請(qǐng)使用brew install nlohmann_json --HEAD

如果您正在使用Meson Build System固棚,那么您可以將此存儲(chǔ)庫(kù)包裝為子項(xiàng)目。

如果您使用Conan來(lái)管理您的依賴項(xiàng)仙蚜,只需添加jsonformoderncpp/x.y.z@vthiery/stableconanfile.py的要求此洲,x.y.z您要使用的發(fā)行版本在哪里。如果您遇到包裝問(wèn)題委粉,請(qǐng)在此處提出問(wèn)題呜师。

如果您使用Spack來(lái)管理依賴項(xiàng),則可以使用該nlohmann_json程序包贾节。有關(guān)包裝的任何問(wèn)題汁汗,請(qǐng)參閱spack項(xiàng)目

如果你在項(xiàng)目中使用獵人來(lái)獲取外部依賴關(guān)系栗涂,那么你可以使用nlohmann_json包知牌。有關(guān)包裝的任何問(wèn)題,請(qǐng)參閱獵人項(xiàng)目斤程。

如果您使用的是Buckaroo角寸,則可以安裝此庫(kù)的模塊buckaroo install nlohmann/json。請(qǐng)在這里提出問(wèn)題忿墅。

如果您在項(xiàng)目中使用vcpkg來(lái)獲取外部依賴項(xiàng)扁藕,那么您可以使用nlohmann-json包。有關(guān)包裝的任何問(wèn)題疚脐,請(qǐng)參閱vcpkg項(xiàng)目亿柑。

如果您使用的是cget,則可以安裝最新的開(kāi)發(fā)版本cget install nlohmann/json棍弄⊥。可以安裝特定版本cget install nlohmann/json@v3.1.0。此外照卦,可以通過(guò)添加-DJSON_MultipleHeaders=ON標(biāo)志(即式矫,cget install nlohmann/json -DJSON_MultipleHeaders=ON)來(lái)安裝多標(biāo)頭版本。

如果您使用的是CocoaPods役耕,則可以通過(guò)將pod添加"nlohmann_json", '~>3.1.2'到podfile 來(lái)使用該庫(kù)(請(qǐng)參閱示例)采转。請(qǐng)在這里提出問(wèn)題。

例子

除了下面的示例,您可能需要查看每個(gè)函數(shù)(包含單獨(dú)代碼示例)的文檔(例如故慈,查看emplace())板熊。所有示例文件都可以自己編譯和執(zhí)行(例如,文件emplace.cpp)察绷。

JSON作為一級(jí)的數(shù)據(jù)類(lèi)型

以下是一些示例干签,可以讓您了解如何使用該類(lèi)。

假設(shè)您要?jiǎng)?chuàng)建如下的JSON對(duì)象:

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

你可以這樣寫(xiě):

// create an empty structure (null)
json j;

// add a number that is stored as double (note the implicit conversion of j to an object)
j["pi"] = 3.141;

// add a Boolean that is stored as bool
j["happy"] = true;

// add a string that is stored as std::string
j["name"] = "Niels";

// add another null object by passing nullptr
j["nothing"] = nullptr;

// add an object inside the object
j["answer"]["everything"] = 42;

// add an array that is stored as std::vector (using an initializer list)
j["list"] = { 1, 0, 2 };

// add another object (using an initializer list of pairs)
j["object"] = { {"currency", "USD"}, {"value", 42.99} };

// instead, you could also write (which looks very similar to the JSON above)
json j2 = {
  {"pi", 3.141},
  {"happy", true},
  {"name", "Niels"},
  {"nothing", nullptr},
  {"answer", {
    {"everything", 42}
  }},
  {"list", {1, 0, 2}},
  {"object", {
    {"currency", "USD"},
    {"value", 42.99}
  }}
};

請(qǐng)注意拆撼,在所有這些情況下容劳,您永遠(yuǎn)不需要“告訴”編譯器您要使用哪種JSON值類(lèi)型。如果你想顯性指定類(lèi)型或表達(dá)一些特定的意圖闸度,函數(shù):json::arrayjson::object可滿足您的需求:

// a way to express the empty array []
json empty_array_explicit = json::array();

// ways to express the empty object {}
json empty_object_implicit = json({});
json empty_object_explicit = json::object();

// a way to express an _array_ of key/value pairs [["currency", "USD"], ["value", 42.99]]
json array_not_object = json::array({ {"currency", "USD"}, {"value", 42.99} });

序列化/反序列化

To/from strings

您可以通過(guò)附加_json到字符串來(lái)創(chuàng)建JSON值(反序列化):

// create object from string literal
json j = "{ \"happy\": true, \"pi\": 3.141 }"_json;

// or even nicer with a raw string literal
auto j2 = R"(
  {
    "happy": true,
    "pi": 3.141
  }
)"_json;

請(qǐng)注意竭贩,如果不附加_json后綴,則不會(huì)解析傳遞的字符串文字莺禁,而只是用作JSON字符串值留量。也就是說(shuō),json j = "{ \"happy\": true, \"pi\": 3.141 }"只是存儲(chǔ)字符串"{ "happy": true, "pi": 3.141 }"而不是解析實(shí)際對(duì)象哟冬。

以上示例也可以使用json::parse()明確表達(dá):

// parse explicitly
auto j3 = json::parse("{ \"happy\": true, \"pi\": 3.141 }");

您還可以獲取JSON值的字符串表示形式(序列化):

// explicit conversion to string
std::string s = j.dump();    // {\"happy\":true,\"pi\":3.141}

// serialization with pretty printing
// pass in the amount of spaces to indent
std::cout << j.dump(4) << std::endl;
// {
//     "happy": true,
//     "pi": 3.141
// }

注意序列化和賦值之間的區(qū)別:

// store a string in a JSON value
json j_string = "this is a string";

// retrieve the string value (implicit JSON to std::string conversion)
std::string cpp_string = j_string;
// retrieve the string value (explicit JSON to std::string conversion)
auto cpp_string2 = j_string.get<std::string>();

// retrieve the serialized value (explicit JSON serialization)
std::string serialized_string = j_string.dump();

// output of original string
std::cout << cpp_string << " == " << cpp_string2 << " == " << j_string.get<std::string>() << '\n';
// output of serialized value
std::cout << j_string << " == " << serialized_string << std::endl;

.dump()始終返回序列化值楼熄,而.get<std::string>()返回最初存儲(chǔ)的字符串值。

請(qǐng)注意浩峡,該庫(kù)僅支持UTF-8可岂。當(dāng)您在庫(kù)中存儲(chǔ)具有不同編碼的字符串時(shí),調(diào)用dump()可能會(huì)拋出異常翰灾。

To/from streams (e.g. files, string streams)

您還可以使用流來(lái)序列化和反序列化:

// deserialize from standard input
json j;
std::cin >> j;

// serialize to standard output
std::cout << j;

// the setw manipulator was overloaded to set the indentation for pretty printing
std::cout << std::setw(4) << j << std::endl;

這些運(yùn)算符適用于std::istreamstd::ostream的任何子類(lèi)青柄。這是文件的類(lèi)似示例:

// read a JSON file
std::ifstream i("file.json");
json j;
i >> j;

// write prettified JSON to another file
std::ofstream o("pretty.json");
o << std::setw(4) << j << std::endl;

請(qǐng)注意,為failbit設(shè)置異常位不適用此用例预侯。由于使用了noexcept說(shuō)明符致开,它將導(dǎo)致程序終止。

從迭代器范圍讀取

您還可以從迭代器范圍解析JSON; 也就是說(shuō)萎馅,其存儲(chǔ)內(nèi)容為連續(xù)的字節(jié)序列双戳,可從迭代器訪問(wèn)的任何容器,例如std::vector<std::uint8_t>:

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

您也可以去掉范圍[begin, end)操作符:

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

SAX interface(待補(bǔ)充)

STL-like access

我們定義的JSON類(lèi)的行為與STL容器一樣糜芳。事實(shí)上飒货,它遵循ReversibleContainer規(guī)范。

// create an array using push_back
json j;
j.push_back("foo");
j.push_back(1);
j.push_back(true);

// also use emplace_back
j.emplace_back(1.78);

// iterate the array
for (json::iterator it = j.begin(); it != j.end(); ++it) {
  std::cout << *it << '\n';
}

// range-based 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);

// comparison
j == "[\"foo\", 1, true]"_json;  // true

// other stuff
j.size();     // 3 entries
j.empty();    // false
j.type();     // json::value_t::array
j.clear();    // the array is empty again

// convenience type checkers
j.is_null();
j.is_boolean();
j.is_number();
j.is_object();
j.is_array();
j.is_string();

// create an object
json o;
o["foo"] = 23;
o["bar"] = false;
o["baz"] = 3.141;

// also use emplace
o.emplace("weather", "sunny");

// special iterator member functions for objects
for (json::iterator it = o.begin(); it != o.end(); ++it) {
  std::cout << it.key() << " : " << it.value() << "\n";
}

// find an entry
if (o.find("foo") != o.end()) {
  // there is an entry with key "foo"
}

// or simpler using count()
int foo_present = o.count("foo"); // 1
int fob_present = o.count("fob"); // 0

// delete an entry
o.erase("foo");

從STL容器轉(zhuǎn)換

任何序列容器(std::array峭竣,std::vector塘辅,std::deque,std::forward_list皆撩,std::list)扣墩,其值可以被用于構(gòu)建JSON值(例如哲银,整數(shù),浮點(diǎn)數(shù)呻惕,布爾值荆责,字符串類(lèi)型,或者在本節(jié)中描述的STL容器)都可被用于創(chuàng)建JSON數(shù)組亚脆。這同樣適用于類(lèi)似的關(guān)聯(lián)容器(std::set做院,std::multiset,std::unordered_set濒持,std::unordered_multiset)键耕,但是在這些情況下,數(shù)組中的元素的順序取決于元素是如何在各個(gè)STL容器排序柑营。

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"]

同樣郁竟,任何鍵值對(duì)容器(std::map,std::multimap由境,std::unordered_map,std::unordered_multimap)蓖议,其鍵可以構(gòu)造一個(gè)std::string虏杰,并且其值可以被用于構(gòu)建JSON值(見(jiàn)上文示例)可用于創(chuàng)建一個(gè)JSON對(duì)象。請(qǐng)注意勒虾,在多映射的情況下纺阔,JSON對(duì)象中只使用一個(gè)鍵,值取決于STL容器的內(nèi)部順序修然。

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}

JSON Pointer和JSON Patch

該庫(kù)支持JSON PointerRFC 6901)作為處理結(jié)構(gòu)化值的替代方法笛钝。最重要的是,JSON PatchRFC 6902)允許描述兩個(gè)JSON值之間的差異 - 有效地允許Unix中已知的patch和diff操作愕宋。

// a JSON value
json j_original = R"({
  "baz": ["one", "two", "three"],
  "foo": "bar"
})"_json;

// access members with a JSON pointer (RFC 6901)
j_original["/baz/1"_json_pointer];
// "two"

// a JSON patch (RFC 6902)
json j_patch = R"([
  { "op": "replace", "path": "/baz", "value": "boo" },
  { "op": "add", "path": "/hello", "value": ["world"] },
  { "op": "remove", "path": "/foo"}
])"_json;

// apply the patch
json j_result = j_original.patch(j_patch);
// {
//    "baz": "boo",
//    "hello": ["world"]
// }

// calculate a JSON patch from two JSON values
json::diff(j_result, j_original);
// [
//   { "op":" replace", "path": "/baz", "value": ["one", "two", "three"] },
//   { "op": "remove","path": "/hello" },
//   { "op": "add", "path": "/foo", "value": "bar" }
// ]

JSON合并Patch

該庫(kù)支持JSON Merge PatchRFC 7386)作為補(bǔ)丁格式玻靡。它不是使用JSON指針(參見(jiàn)上文)來(lái)指定要操作的值,而是使用與所修改文檔非常相似的語(yǔ)法來(lái)描述更改中贝。

// a JSON value
json j_document = R"({
  "a": "b",
  "c": {
    "d": "e",
    "f": "g"
  }
})"_json;

// a patch
json j_patch = R"({
  "a":"z",
  "c": {
    "f": null
  }
})"_json;

// apply the patch
j_original.merge_patch(j_patch);
// {
//  "a": "z",
//  "c": {
//    "d": "e"
//  }
// }

隱式轉(zhuǎn)換
JSON對(duì)象的類(lèi)型由要存儲(chǔ)的表達(dá)式自動(dòng)確定囤捻。同樣,隱式轉(zhuǎn)換存儲(chǔ)的值邻寿。

// strings
std::string s1 = "Hello, world!";
json js = s1;
std::string s2 = js;

// Booleans
bool b1 = true;
json jb = b1;
bool b2 = jb;

// numbers
int i = 42;
json jn = i;
double f = jn;

// etc.

您也可以顯式請(qǐng)求值:

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

// etc.

請(qǐng)注意char類(lèi)型不能自動(dòng)轉(zhuǎn)換成JSON字符串蝎土,而是轉(zhuǎn)換成整型數(shù)。要轉(zhuǎn)換成字符串必須顯式指定:

char ch = 'A';                       // ASCII value 65
json j_default = ch;                 // stores integer number 65
json j_string = std::string(1, ch);  // stores string "A"

任意類(lèi)型轉(zhuǎn)換

任何類(lèi)型都可以用JSON序列化绣否,而不僅僅是STL容器和標(biāo)量類(lèi)型誊涯。通常,您將遵循這些準(zhǔn)繩來(lái)做事:

namespace ns {
    // a simple struct to model a person
    struct person {
        std::string name;
        std::string address;
        int age;
    };
}

ns::person p = {"Ned Flanders", "744 Evergreen Terrace", 60};

// convert to JSON: copy each value into the JSON object
json j;
j["name"] = p.name;
j["address"] = p.address;
j["age"] = p.age;

// ...

// convert from JSON: copy each value from the JSON object
ns::person p {
    j["name"].get<std::string>(),
    j["address"].get<std::string>(),
    j["age"].get<int>()
};

這個(gè)代碼沒(méi)有問(wèn)題蒜撮,但是有點(diǎn)啰嗦暴构,我們有一個(gè)更好的辦法:

// create a person
ns::person p {"Ned Flanders", "744 Evergreen Terrace", 60};

// conversion: person -> json
json j = p;

std::cout << j << std::endl;
// {"address":"744 Evergreen Terrace","age":60,"name":"Ned Flanders"}

// conversion: json -> person
ns::person p2 = j;

// that's it
assert(p == p2);

基本用法

要使其適用于您的某種類(lèi)型,您只需提供兩個(gè)功能:

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

就這么簡(jiǎn)單!json使用您的類(lèi)型調(diào)用構(gòu)造函數(shù)時(shí)丹壕,您的自定義方法to_json將被自動(dòng)調(diào)用庆械。同樣,在調(diào)用get<your_type>()時(shí)菌赖,from_json將被自動(dòng)調(diào)用缭乘。
一些重要的點(diǎn):

  • 那些方法必須在你的類(lèi)型的命名空間(可以是全局命名空間)中,否則庫(kù)將無(wú)法找到它們(在這個(gè)例子中琉用,person在命名空間中ns中定義)堕绩。
  • 在您使用隱式轉(zhuǎn)換的地方,那些方法必須是有效的(例如:正確的頭文件必須被包含)查看問(wèn)題1108邑时,了解可能發(fā)生的錯(cuò)誤奴紧。
  • 使用get<your_type>()時(shí),your_type 必須DefaultConstructible晶丘。(后面會(huì)描述一種可以繞過(guò)這個(gè)要求的方法黍氮。)
  • 在函數(shù)from_json中,使用函數(shù)at()來(lái)訪問(wèn)對(duì)象值而不是operator[]浅浮。如果某個(gè)鍵不存在沫浆,則at拋出一個(gè)可以處理的異常,然而operator[]顯示未定義的行為滚秩。
  • 如果您的類(lèi)型包含多個(gè)operator=定義专执,則代碼your_variable = your_json; 可能無(wú)法編譯。您需要改寫(xiě)成your_variable = your_json.get<decltype your_variable>();郁油。
  • 您不需要為STL類(lèi)型添加序列化或反序列化程序本股,例如std::vector:庫(kù)已經(jīng)實(shí)現(xiàn)了這些。
  • 注意函數(shù)from_json/ to_json的定義順序:如果一個(gè)類(lèi)型B有類(lèi)型的成員A桐腌,你必須在定義to_json(B)之前拄显,定義to_json(A)。請(qǐng)查看問(wèn)題561以獲取更多詳細(xì)信息案站。

怎么轉(zhuǎn)換第三方庫(kù)的類(lèi)型凿叠?

這需要更高級(jí)的技術(shù)。首先嚼吞,讓我們來(lái)看看這種轉(zhuǎn)換機(jī)制是如何工作的:
該庫(kù)使用JSON Serializers將類(lèi)型轉(zhuǎn)換成json盒件。nlohmann::json的默認(rèn)序列化程序是nlohmann::adl_serializer (ADL means Argument-Dependent Lookup)。
它是這樣實(shí)現(xiàn)的(簡(jiǎn)化):

template <typename T>
struct adl_serializer {
    static void to_json(json& j, const T& value) {
        // calls the "to_json" method in T's namespace
    }

    static void from_json(const json& j, T& value) {
        // same thing, but with the "from_json" method
    }
};

當(dāng)您控制類(lèi)型的命名空間時(shí)舱禽,此序列化程序可以正常工作炒刁。但是,boost::optional或者std::filesystem::path(C ++ 17)呢誊稚?盜用boost命名空間是非常糟糕的翔始,并且向stl中添加模板特化以外的東西是非法......
為了解決這個(gè)問(wèn)題罗心,您需要向命名空間nlohmann中添加adl_serializer,示例如下:

// partial specialization (full specialization works too)
namespace nlohmann {
   template <typename T>
   struct adl_serializer<boost::optional<T>> {
       static void to_json(json& j, const boost::optional<T>& opt) {
           if (opt == boost::none) {
               j = nullptr;
           } else {
             j = *opt; // this will call adl_serializer<T>::to_json which will
                       // find the free function to_json in T's namespace!
           }
       }

       static void from_json(const json& j, boost::optional<T>& opt) {
           if (j.is_null()) {
               opt = boost::none;
           } else {
               opt = j.get<T>(); // same as above, but with
                                 // adl_serializer<T>::from_json
           }
       }
   };
}

如何在沒(méi)有默認(rèn)構(gòu)造函數(shù)或不可拷貝的類(lèi)型使用get()城瞎?

有個(gè)辦法渤闷,如果您的類(lèi)型是MoveConstructible的。您也將需要特化adl_serializer脖镀,重載from_json

struct move_only_type {
    move_only_type() = delete;
    move_only_type(int ii): i(ii) {}
    move_only_type(const move_only_type&) = delete;
    move_only_type(move_only_type&&) = default;

    int i;
};

namespace nlohmann {
    template <>
    struct adl_serializer<move_only_type> {
        // note: the return type is no longer 'void', and the method only takes
        // one argument
        static move_only_type from_json(const json& j) {
            return {j.get<int>()};
        }

        // Here's the catch! You must provide a to_json method! Otherwise you
        // will not be able to convert move_only_type to json, since you fully
        // specialized adl_serializer on that type
        static void to_json(json& j, move_only_type t) {
            j = t.i;
        }
    };
}

怎么編寫(xiě)自己的序列化程序飒箭?(高級(jí)用途)

您可以看下在測(cè)試套件unit-udt.cpp查看一些示例。
如果您編寫(xiě)自己的序列化程序蜒灰,則需要執(zhí)行以下操作:

  • 對(duì)basic_json弦蹂,使用與 nlohmann::json中不同的別名(basic_json的最后一個(gè)模板參數(shù)是JSONSerializer
  • 在您所有的to_json/from_json函數(shù)中,使用您對(duì)basic_json起的別名(或模板參數(shù))强窖。
  • 當(dāng)您需要ADL時(shí)凸椿,使用nlohmann::to_jsonnlohmann::from_json

下面是一個(gè)示例,只接受大小<=32的類(lèi)型并且使用ADL翅溺。

// You should use void as a second template argument
// if you don't need compile-time checks on T
template<typename T, typename SFINAE = typename std::enable_if<sizeof(T) <= 32>::type>
struct less_than_32_serializer {
    template <typename BasicJsonType>
    static void to_json(BasicJsonType& j, T value) {
        // we want to use ADL, and call the correct to_json overload
        using nlohmann::to_json; // this method is called by adl_serializer,
                                 // this is where the magic happens
        to_json(j, value);
    }

    template <typename BasicJsonType>
    static void from_json(const BasicJsonType& j, T& value) {
        // same thing here
        using nlohmann::from_json;
        from_json(j, value);
    }
};

重新實(shí)現(xiàn)序列化程序時(shí)脑漫,要非常小心,如果不注意咙崎,可能堆棧溢出:

template <typename T, void>
struct bad_serializer
{
    template <typename BasicJsonType>
    static void to_json(BasicJsonType& j, const T& value) {
      // this calls BasicJsonType::json_serializer<T>::to_json(j, value);
      // if BasicJsonType::json_serializer == bad_serializer ... oops!
      j = value;
    }

    template <typename BasicJsonType>
    static void to_json(const BasicJsonType& j, T& value) {
      // this calls BasicJsonType::json_serializer<T>::from_json(j, value);
      // if BasicJsonType::json_serializer == bad_serializer ... oops!
      value = j.template get<T>(); // oops!
    }
};

二進(jìn)制格式(CBOR, MessagePack, and UBJSON)

雖然JSON是一種普遍存在的數(shù)據(jù)格式优幸,但它并不是一種非常緊湊的格式,適用于數(shù)據(jù)交換叙凡,例如通過(guò)網(wǎng)絡(luò)。因?yàn)槊苣搸?kù)支持CBOR (簡(jiǎn)明二進(jìn)制對(duì)象表示)握爷,MessagePackUBJSON (通用二進(jìn)制規(guī)范)以有效地將JSON值編碼為字節(jié)向量和解碼此類(lèi)向量。

// create a JSON value
json j = R"({"compact": true, "schema": 0})"_json;

// 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);
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末严里,一起剝皮案震驚了整個(gè)濱河市新啼,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌刹碾,老刑警劉巖燥撞,帶你破解...
    沈念sama閱讀 216,496評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異迷帜,居然都是意外死亡物舒,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)戏锹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)冠胯,“玉大人,你說(shuō)我怎么就攤上這事锦针≤欤” “怎么了置蜀?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,632評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)悉盆。 經(jīng)常有香客問(wèn)我盯荤,道長(zhǎng),這世上最難降的妖魔是什么焕盟? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,180評(píng)論 1 292
  • 正文 為了忘掉前任秋秤,我火速辦了婚禮,結(jié)果婚禮上京髓,老公的妹妹穿的比我還像新娘航缀。我一直安慰自己,他們只是感情好堰怨,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,198評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布芥玉。 她就那樣靜靜地躺著,像睡著了一般备图。 火紅的嫁衣襯著肌膚如雪灿巧。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,165評(píng)論 1 299
  • 那天揽涮,我揣著相機(jī)與錄音抠藕,去河邊找鬼。 笑死蒋困,一個(gè)胖子當(dāng)著我的面吹牛盾似,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播雪标,決...
    沈念sama閱讀 40,052評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼零院,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了村刨?” 一聲冷哼從身側(cè)響起告抄,我...
    開(kāi)封第一講書(shū)人閱讀 38,910評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎嵌牺,沒(méi)想到半個(gè)月后打洼,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,324評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡逆粹,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,542評(píng)論 2 332
  • 正文 我和宋清朗相戀三年募疮,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片僻弹。...
    茶點(diǎn)故事閱讀 39,711評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡酝锅,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出奢方,到底是詐尸還是另有隱情搔扁,我是刑警寧澤爸舒,帶...
    沈念sama閱讀 35,424評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站稿蹲,受9級(jí)特大地震影響扭勉,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜苛聘,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,017評(píng)論 3 326
  • 文/蒙蒙 一涂炎、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧设哗,春花似錦唱捣、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,668評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至战虏,卻和暖如春拣宰,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背烦感。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,823評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工巡社, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人手趣。 一個(gè)月前我還...
    沈念sama閱讀 47,722評(píng)論 2 368
  • 正文 我出身青樓晌该,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親绿渣。 傳聞我的和親對(duì)象是個(gè)殘疾皇子朝群,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,611評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)怯晕,斷路器潜圃,智...
    卡卡羅2017閱讀 134,651評(píng)論 18 139
  • 關(guān)于Mongodb的全面總結(jié) MongoDB的內(nèi)部構(gòu)造《MongoDB The Definitive Guide》...
    中v中閱讀 31,928評(píng)論 2 89
  • 官方中文版原文鏈接 感謝社區(qū)中各位的大力支持缸棵,譯者再次奉上一點(diǎn)點(diǎn)福利:阿里云產(chǎn)品券舟茶,享受所有官網(wǎng)優(yōu)惠,并抽取幸運(yùn)大...
    HetfieldJoe閱讀 2,514評(píng)論 1 17
  • 童年的夢(mèng)繁星點(diǎn)點(diǎn)阀捅,陽(yáng)光下的我們?cè)诔砷L(zhǎng),時(shí)光留下了我們的腳步针余, 光陰似箭饲鄙,日月如梭凄诞。“童年”兩個(gè)字漸...
    紫東閱讀 301評(píng)論 0 2
  • 媽媽12點(diǎn)半進(jìn)手術(shù)室忍级,1點(diǎn)多寶寶的胎盤(pán)出來(lái)了帆谍,1點(diǎn)7分寶寶出生,按照時(shí)差大概1點(diǎn)37分出生轴咱。 爸爸抱著寶寶汛蝙,寶寶左...
    miaoyin閱讀 228評(píng)論 0 0