json
對象那裡有無數的 JSON 庫,每個庫甚至都有其存在的理由。我們班有以下設計目標:
直觀的語法。在 Python 等語言中,JSON 感覺像是一流的資料類型。我們使用了現代 C++ 的所有運算子魔法來在程式碼中實現相同的感覺。看看下面的例子,你就會明白我的意思。
瑣碎的整合。我們的整個程式碼由一個頭檔json.hpp
組成。就是這樣。沒有函式庫,沒有子項目,沒有依賴項,沒有複雜的建置系統。該類別是用普通 C++11 編寫的。總而言之,一切都不需要調整編譯器標誌或專案設定。
認真測試。我們的程式碼經過嚴格的單元測試,涵蓋了 100% 的程式碼,包括所有異常行為。此外,我們還與 Valgrind 和 Clang Sanitizers 進行了檢查,確認不存在記憶體洩漏。 Google OSS-Fuzz 也對所有解析器 24/7 執行模糊測試,迄今為止有效執行了數十億次測試。為了保持高品質,該專案遵循核心基礎設施計劃 (CII) 最佳實踐。
其他方面對我們來說並不那麼重要:
記憶效率。每個 JSON 物件都有一個指標(聯合的最大大小)和一個枚舉元素(1 個位元組)的開銷。預設泛化使用以下 C++ 資料型別: std::string
表示字串, int64_t
、 uint64_t
或double
表示數字, std::map
表示對象, std::vector
表示數組, bool
表示布林值。但是,您可以根據需要對通用類別basic_json
進行模板化。
速度。當然有更快的 JSON 庫。但是,如果您的目標是透過使用單一標頭添加 JSON 支援來加快開發速度,那麼這個程式庫就是您的最佳選擇。如果您知道如何使用std::vector
或std::map
,那麼您已經準備好了。
有關更多信息,請參閱貢獻指南。
您可以在 GitHub Sponsors 上贊助該庫。
謝謝大家!
❓ 如果您有疑問,請檢查常見問題或問答部分是否已得到解答。如果沒有,請在那裡提出一個新問題。
如果您想了解有關如何使用該庫的更多信息,請查看自述文件的其餘部分,查看程式碼範例,或瀏覽幫助頁面。
?如果您想更了解API ,請查看API 參考。
?如果您發現錯誤,請查看常見問題(它是已知問題還是設計決策的結果)。在建立新問題之前,也請查看問題清單。請提供盡可能多的信息,以幫助我們理解和重現您的問題。
還有一個適用於文件瀏覽器 Dash、Velocity 和 Zeal的文檔集,其中包含作為離線資源的完整文件。
以下是一些範例,可協助您了解如何使用該類別。
除了下面的範例之外,您可能還想:
→ 檢視文檔
→ 瀏覽獨立範例文件
每個 API 函數(在 API 文件中記錄)都有一個對應的獨立範例文件。例如, emplace()
函數有一個相符的 emplace.cpp 範例檔。
json
類別提供了用於操作 JSON 值的 API。透過讀取 JSON 檔案來建立json
物件:
# include < fstream >
# include < nlohmann/json.hpp >
using json = nlohmann::json;
// ...
std::ifstream f ( " example.json " );
json data = json::parse(f);
json
對象假設您想要在檔案中建立硬編碼這個文字 JSON 值作為json
物件:
{
"pi" : 3.141 ,
"happy" : true
}
有多種選擇:
// Using (raw) string literals and json::parse
json ex1 = json::parse( R"(
{
"pi": 3.141,
"happy": true
}
)" );
// Using user-defined (raw) string literals
using namespace nlohmann ::literals ;
json ex2 = R"(
{
"pi": 3.141,
"happy": true
}
)" _json;
// Using initializer lists
json ex3 = {
{ " happy " , true },
{ " pi " , 3.141 },
};
以下是一些範例,可協助您了解如何使用該類別。
假設您要建立 JSON 對象
{
"pi" : 3.141 ,
"happy" : true ,
"name" : " Niels " ,
"nothing" : null ,
"answer" : {
"everything" : 42
},
"list" : [ 1 , 0 , 2 ],
"object" : {
"currency" : " USD " ,
"value" : 42.99
}
}
有了這個庫,你可以寫:
// 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 }
}}
};
請注意,在所有這些情況下,您永遠不需要「告訴」編譯器您要使用哪種 JSON 值類型。如果您想明確或表達一些邊緣情況,函數json::array()
和json::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 } });
您可以透過將_json
附加到字串文字來建立 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;
請注意,如果不附加_json
後綴,則不會解析傳遞的字串文字,而只是用作 JSON 字串值。也就是說, json j = "{ "happy": true, "pi": 3.141 }"
只會儲存字串"{ "happy": true, "pi": 3.141 }"
而非解析實際的字串目的。
應using namespace nlohmann::literals;
(參見json::parse()
)。
上面的範例也可以使用json::parse()
來明確表示:
// parse explicitly
auto j3 = json::parse( R"( {"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
// }
注意序列化和賦值之間的差異:
// store a string in a JSON value
json j_string = " this is a string " ;
// retrieve the string value
auto cpp_string = j_string. template get<std::string>();
// retrieve the string value (alternative when a variable already exists)
std::string cpp_string2;
j_string.get_to(cpp_string2);
// 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. template get<std::string>() << ' n ' ;
// output of serialized value
std::cout << j_string << " == " << serialized_string << std::endl;
.dump()
傳回原始儲存的字串值。
請注意,該庫僅支援 UTF-8。當您在庫中儲存具有不同編碼的字串時,呼叫dump()
可能會引發異常,除非使用json::error_handler_t::replace
或json::error_handler_t::ignore
作為錯誤處理程序。
您也可以使用流來序列化和反序列化:
// 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;
這些運算子適用於std::istream
或std::ostream
的任何子類別。這是與文件相同的範例:
// 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;
請注意,為failbit
位元設定異常位元不適合此用例。由於使用了noexcept
說明符,它將導致程式終止。
您也可以從迭代器範圍解析 JSON;也就是說,來自任何可由迭代器存取的容器,其value_type
是 1、2 或 4 個位元組的整數類型,這將分別解釋為 UTF-8、UTF-16 和 UTF-32。例如, std::vector<std::uint8_t>
或std::list<std::uint16_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);
由於解析函數接受任意迭代器範圍,因此您可以透過實作LegacyInputIterator
概念來提供自己的資料來源。
struct MyContainer {
void advance ();
const char & get_current ();
};
struct MyIterator {
using difference_type = std:: ptrdiff_t ;
using value_type = char ;
using pointer = const char *;
using reference = const char &;
using iterator_category = std::input_iterator_tag;
MyIterator& operator ++() {
target-> advance ();
return * this ;
}
bool operator !=( const MyIterator& rhs) const {
return rhs. target != target;
}
reference operator *() const {
return target-> get_current ();
}
MyContainer* target = nullptr ;
};
MyIterator begin (MyContainer& tgt) {
return MyIterator{&tgt};
}
MyIterator end ( const MyContainer&) {
return {};
}
void foo () {
MyContainer c;
json j = json::parse (c);
}
該庫使用類似 SAX 的接口,具有以下功能:
// called when null is parsed
bool null ();
// called when a boolean is parsed; value is passed
bool boolean ( bool val);
// called when a signed or unsigned integer number is parsed; value is passed
bool number_integer ( number_integer_t val);
bool number_unsigned ( number_unsigned_t val);
// called when a floating-point number is parsed; value and original string is passed
bool number_float ( number_float_t val, const string_t & s);
// called when a string is parsed; value is passed and can be safely moved away
bool string ( string_t & val);
// called when a binary value is parsed; value is passed and can be safely moved away
bool binary ( binary_t & val);
// called when an object or array begins or ends, resp. The number of elements is passed (or -1 if not known)
bool start_object (std:: size_t elements);
bool end_object ();
bool start_array (std:: size_t elements);
bool end_array ();
// called when an object key is parsed; value is passed and can be safely moved away
bool key ( string_t & val);
// called when a parse error occurs; byte position, the last token, and an exception is passed
bool parse_error (std:: size_t position, const std::string& last_token, const detail:: exception & ex);
每個函數的回傳值決定是否應該繼續解析。
若要實作您自己的 SAX 處理程序,請依照下列步驟操作:
nlohmann::json_sax<json>
作為基底類,但也可以使用實作上述功能且公開的任何類別。my_sax
。bool json::sax_parse(input, &my_sax)
;其中第一個參數可以是任何輸入,例如字串或輸入流,第二個參數是指向 SAX 介面的指標。請注意, sax_parse
函數僅傳回一個bool
指示上次執行的 SAX 事件的結果。它不傳回json
值 - 由您決定如何處理 SAX 事件。此外,如果出現解析錯誤,則不會引發異常 - 由您決定如何處理傳遞給parse_error
實作的異常物件。在內部,SAX 介面用於 DOM 解析器(類別json_sax_dom_parser
)以及接受器( json_sax_acceptor
),請參閱檔案json_sax.hpp
。
我們設計的 JSON 類別的行為就像 STL 容器一樣。事實上,它滿足了ReversibleContainer 的要求。
// 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 auto tmp = j[ 0 ]. template get<std::string>();
j[ 1 ] = 42 ;
bool foo = j.at( 2 );
// comparison
j == R"( ["foo", 1, true, 1.78] )" _json; // true
// other stuff
j.size(); // 4 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 " ;
}
// the same code as range for
for ( auto & el : o.items()) {
std::cout << el. key () << " : " << el. value () << " n " ;
}
// even easier with structured bindings (C++17)
for ( auto & [key, value] : o.items()) {
std::cout << key << " : " << value << " n " ;
}
// find an entry
if (o.contains( " foo " )) {
// there is an entry with key "foo"
}
// or via find and an iterator
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 " );
任何序列容器( std::array
、 std::vector
、 std::deque
、 std::forward_list
、 std::list
),其值可用於建構 JSON 值(例如整數、浮點數、布林值、字串)類型,或本節中所述的STL 容器)可用於建立JSON 陣列。對於類似的關聯容器( std::set
、 std::multiset
、 std::unordered_set
、 std::unordered_multiset
)也是如此,但在這些情況下,陣列元素的順序取決於元素在陣列中的排序方式。的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"]
同樣,任何關聯鍵值容器( std::map
、 std::multimap
、 std::unordered_map
、 std::unordered_multimap
),其鍵可以建構std::string
並且其值可用於建構 JSON 值(請參閱上面的範例)可用於建立JSON 物件。請注意,在多重映射的情況下,JSON 物件中僅使用一個鍵,且該值取決於 STL 容器的內部順序。
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 指標(RFC 6901) 作為尋址結構化值的替代方法。除此之外, JSON Patch (RFC 6902) 允許描述兩個 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 Merge Patch (RFC 7386) 作為補丁格式。它沒有使用 JSON 指標(見上文)來指定要操作的值,而是使用密切模仿正在修改的文件的語法來描述變更。
// 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_document.merge_patch(j_patch);
// {
// "a": "z",
// "c": {
// "d": "e"
// }
// }
支援的類型可以隱式轉換為 JSON 值。
建議不要使用JSON 值的隱式轉換。您可以在此處找到有關此建議的更多詳細資訊。您可以透過在包含json.hpp
標頭之前將JSON_USE_IMPLICIT_CONVERSIONS
定義為0
來關閉隱式轉換。使用 CMake 時,您也可以透過將選項JSON_ImplicitConversions
設定為OFF
來實現此目的。
// strings
std::string s1 = " Hello, world! " ;
json js = s1;
auto s2 = js. template get<std::string>();
// NOT RECOMMENDED
std::string s3 = js;
std::string s4;
s4 = js;
// Booleans
bool b1 = true ;
json jb = b1;
auto b2 = jb. template get< bool >();
// NOT RECOMMENDED
bool b3 = jb;
bool b4;
b4 = jb;
// numbers
int i = 42