Json文件解析(上)
代碼地址:https://github.com/nlohmann/json
- 設計目標
- 贊助商
- 積分
- 例子
- JSON作為一流的數據類型
- 序列化/反序列化
- 類似STL的訪問
- 從STL容器轉換
- JSON指針和JSON補丁
- JSON合並補丁
- 隱式轉換
- 到/從任意類型的轉換
- 專門進行枚舉轉換
- 二進制格式(BSON,CBOR,MessagePack和UBJSON)
- 支持的編譯器
- 執照
- 聯系
- 謝謝
- 二手第三方工具
- 使用JSON for Modern C ++的項目
- 筆記
- 執行單元測試
設計目標
那里有無數的JSON庫,每個庫甚至都有其存在的理由。班有以下設計目標:
- 直觀的語法。在Python等語言中,JSON感覺就像是一流的數據類型。使用了現代C ++的所有操作符魔術,以在代碼中實現相同的感覺。查看以下示例,將了解意思。
- 微不足道的整合。整個代碼包含一個頭文件json.hpp。沒有庫,沒有子項目,沒有依賴項,沒有復雜的構建系統。該類用香草C ++ 11編寫。總而言之,一切都不需要調整編譯器標志或項目設置。
- 認真測試。項目經過嚴格的單元測試,涵蓋了100%的代碼,包括所有異常行為。此外,使用Valgrind和Clang網絡檢查是否有內存泄漏。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,那么已經設置好了。
積分
json.hpp是此處single_include/nlohmann或發布的單個必需文件。需要添加
#包括 < nlohmann / json.hpp >
//為了方便
使用 json = nlohmann :: json;
到要處理JSON的文件,並設置必要的開關以啟用C ++ 11(例如,-std=c++11對於GCC和Clang)。
可以進一步使用file include/nlohmann/json_fwd.hpp進行前向聲明。json_fwd.hpp的安裝(作為cmake安裝步驟的一部分)可以通過設置來實現-DJSON_MultipleHeaders=ON。
CMake的
也可以nlohmann_json::nlohmann_json在CMake中使用接口目標。此目標填充適當的使用要求,INTERFACE_INCLUDE_DIRECTORIES以指向適當的包含目錄和INTERFACE_COMPILE_FEATURES必要的C ++ 11標志。
外部
要從CMake項目中使用此庫,可以直接從中找到,find_package()並使用生成的程序包配置中的命名空間導入目標:
#CMakeLists.txt
find_package(nlohmann_json 3.2.0 REQUIRED)
...
add_library(foo ...)
...
target_link_libraries(foo PRIVATE nlohmann_json :: nlohmann_json)
軟件包配置文件nlohmann_jsonConfig.cmake可以在安裝樹中使用,也可以在構建樹之外直接使用。
嵌入式的
要將庫直接嵌入到現有CMake項目中,請將整個源代碼樹放置在子目錄中,然后調用add_subdirectory()CMakeLists.txt文件:
#通常不那么關心第三方庫的測試是
#從自己的項目的代碼運行。
設置(JSON_BuildTests OFF CACHE INTERNAL “”)
#如果僅在私有源文件中包含此第三方,則
#在安裝主項目時不需要安裝。
#集(JSON_Install OFF CACHE INTERNAL “”)
#請勿使用include(nlohmann_json / CMakeLists.txt),因為會附帶
#會導致構建中斷的意外后果。通常
#鼓勵(雖然不一定有據可查這樣)使用
#包括(...),用於其項目的CMake反正拉動。
add_sub目錄(nlohmann_json)
...
add_library(foo ...)
...
target_link_libraries(foo PRIVATE nlohmann_json :: nlohmann_json)
嵌入式(FetchContent)
從CMake v3.11開始, FetchContent可以作為配置類型的依賴項自動下載資源庫。
例:
包括(FetchContent)
FetchContent_Declare(json
GIT_REPOSITORY https://github.com/nlohmann/json.git
GIT_TAG v3.7.3)
FetchContent_GetProperties(json)
如果(NOT json_POPULATED)
FetchContent_Populate(json)
add_subdirectory($ {json_SOURCE_DIR} $ {json_BINARY_DIR} EXCLUDE_FROM_ALL)
endif()
target_link_libraries(foo PRIVATE nlohmann_json :: nlohmann_json)
注意:倉庫https://github.com/nlohmann/json的下載量很大。包含用於基准測試的所有數據集。可能需要依賴較小的存儲庫。例如,可能想用https://github.com/ArthurSonzogni/nlohmann_json_cmake_fetchcontent替換上面的URL
同時支持
為了允許項目支持外部提供的或嵌入式JSON庫,可以使用類似於以下內容的模式:
# Top level CMakeLists.txt
project(FOO)
...
option(FOO_USE_EXTERNAL_JSON "Use an external JSON library" OFF)
...
add_subdirectory(thirdparty)
...
add_library(foo ...)
...
# Note that the namespaced target will always be available regardless of the
# import method
target_link_libraries(foo PRIVATE nlohmann_json::nlohmann_json)
# thirdparty/CMakeLists.txt
...
if(FOO_USE_EXTERNAL_JSON)
find_package(nlohmann_json 3.2.0 REQUIRED)
else()
set(JSON_BuildTests OFF CACHE INTERNAL "")
add_subdirectory(nlohmann_json)
endif()
...
thirdparty/nlohmann_json 然后是此源樹的完整副本。
包管理Package Managers
If you are using OS X and Homebrew, just type brew tap nlohmann/json
and brew install nlohmann-json
and you're set. If you want the bleeding edge rather than the latest release, use brew install nlohmann-json --HEAD
.
If you are using the Meson Build System, add this source tree as a meson subproject. You may also use the include.zip
published in this project's Releases to reduce the size of the vendored source tree. Alternatively, you can get a wrap file by downloading it from Meson WrapDB, or simply use meson wrap install nlohmann_json
. Please see the meson project for any issues regarding the packaging.
The provided meson.build can also be used as an alternative to cmake for installing nlohmann_json
system-wide in which case a pkg-config file is installed. To use it, simply have your build system require the nlohmann_json
pkg-config dependency. In Meson, it is preferred to use the dependency()
object with a subproject fallback, rather than using the subproject directly.
If you are using Conan to manage your dependencies, merely add nlohmann_json/x.y.z
to your conanfile
's requires, where x.y.z
is the release version you want to use. Please file issues here if you experience problems with the packages.
If you are using Spack to manage your dependencies, you can use the nlohmann-json
package. Please see the spack project for any issues regarding the packaging.
If you are using hunter on your project for external dependencies, then you can use the nlohmann_json package. Please see the hunter project for any issues regarding the packaging.
If you are using Buckaroo, you can install this library's module with buckaroo add github.com/buckaroo-pm/nlohmann-json
. Please file issues here. There is a demo repo here.
If you are using vcpkg on your project for external dependencies, then you can use the nlohmann-json package. Please see the vcpkg project for any issues regarding the packaging.
If you are using cget, you can install the latest development version with cget install nlohmann/json
. A specific version can be installed with cget install nlohmann/json@v3.1.0
. Also, the multiple header version can be installed by adding the -DJSON_MultipleHeaders=ON
flag (i.e., cget install nlohmann/json -DJSON_MultipleHeaders=ON
).
If you are using CocoaPods, you can use the library by adding pod "nlohmann_json", '~>3.1.2'
to your podfile (see an example). Please file issues here.
If you are using NuGet, you can use the package nlohmann.json. Please check this extensive description on how to use the package. Please files issues here.
If you are using conda, you can use the package nlohmann_json from conda-forge executing conda install -c conda-forge nlohmann_json
. Please file issues here.
If you are using MSYS2, your can use the mingw-w64-nlohmann-json package, just type pacman -S mingw-w64-i686-nlohmann-json
or pacman -S mingw-w64-x86_64-nlohmann-json
for installation. Please file issues here if you experience problems with the packages.
If you are using build2
, you can use the nlohmann-json
package from the public repository https://cppget.org or directly from the package's sources repository. In your project's manifest
file, just add depends: nlohmann-json
(probably with some version constraints). If you are not familiar with using dependencies in build2
, please read this introduction. Please file issues here if you experience problems with the packages.
If you are using wsjcpp
, you can use the command wsjcpp install "https://github.com/nlohmann/json:develop"
to get the latest version. Note you can change the branch ":develop" to an existing tag or another branch。
包配置
如果使用的是裸Makefile,則可以使用pkg-config生成指向庫安裝位置的include標志:
pkg-config nlohmann_json --cflags
Meson構建系統的用戶還可以使用系統范圍的庫,該庫可以通過pkg-config以下方式找到:
json = dependency('nlohmann_json', required: true)
例子
除了下面的示例,可能想查看文檔,其中每個函數都包含一個單獨的代碼示例(例如,檢出emplace())。所有示例文件都可以單獨編譯和執行(例如,文件emplace.cpp)。
JSON作為一流的數據類型
以下是一些示例,可讓了解如何使用該類。
假設要創建JSON對象
{
"pi": 3.141,
"happy": true,
"name": "Niels",
"nothing": null,
"answer": {
"everything": 42
},
"list": [1, 0, 2],
"object": {
"currency": "USD",
"value": 42.99
}
}
使用該庫,可以編寫:
//創建一個空結構(空)
json j;
//添加一個存儲為double的數字(注意j到對象的隱式轉換)
j [ “ pi ” ] = 3.141 ;
//添加一個存儲為bool
j [ “ happy ” ] = true的布爾值;
//添加一個存儲為std :: string
j [ “ name ” ] = “ Niels ”的字符串;
//通過傳遞nullptr
j [ “ nothing ” ] = nullptr來添加另一個null對象;
//在對象
j [ “ answer ” ] [ “ everything ” ] = 42 內添加一個對象;
//添加存儲為標准::向量(使用初始化列表)的陣列
f] [ “列表” ] = { 1, 0, 2 };
//添加另一個對象(使用成對的初始化列表)
j [ “ object ” ] = {{ “ currency ”, “ USD ” },{ “ value ”, 42.99 }};
//相反,也可以編寫(看起來與上面的JSON非常相似)
json j2 = {
{ “ pi ”,3.141 },
{ “ happy ”,true },
{ “ name ”,“ Niels ” },
{ “沒有”,nullptr },
{ “答案”,{
{ “一切”,42 }
}},
{ “列表”,{ 1,0,2 }},
{ “對象”,{
{ “貨幣”,“美元” },
{ “值”,42.99 }
}}
};
請注意,在所有這些情況下,都無需“告訴”編譯器要使用的JSON值類型。如果想明確表達或表達一些極端情況,這些函數json::array()和json::object()將有助於:
//表達空數組[]的方法
json empty_array_explicit = json :: array();
//表示空對象的方法{}
json empty_object_implicit = json({});
json empty_object_explicit = json :: object();
//一種方式來表達的鍵/值對的_array_ [[ “貨幣”, “USD”],[ “值”,42.99]]
JSON array_not_object = JSON ::陣列({{ “貨幣”, “ USD ” } ,{ “ value ”, 42.99 }});
序列化/反序列化
到/從字符串
可以通過附加_json到字符串文字來創建JSON值(反序列化):
//從字符串文字
json創建對象 j = “ { \” happy \“:true,\” pi \“: 3.141 } ” _json;
//甚至使用原始字符串文字
自動 j2 = R“(
{
” happy“:true,
” pi“:
3.141 }
)” _json;
請注意,不附加_json后綴,傳遞的字符串文字不被解析,而僅用作JSON字符串值。也就是說,json j = "{ \"happy\": true, \"pi\": 3.141 }"將只存儲字符串"{ "happy": true, "pi": 3.141 }"而不是解析實際對象。
上面的示例也可以使用來明確表示json::parse():
//明確解析
自動 j3 = json :: parse( “ { \” happy \“:true,\” pi \“:3.141} ”);
還可以獲取JSON值的字符串表示形式(序列化):
//明確轉換為字符串
std :: string s = j.dump(); // {“ happy”:true,“ pi”:3.141}
//通過漂亮的打印進行序列化
//傳遞空格以縮進
std :: cout << j.dump( 4)<< std :: endl;
// {
// “ happy”:true,
// “ pi”:3.141
// }
注意序列化和賦值之間的區別:
//在JSON值中存儲一個字符串
json j_string = “這是一個字符串” ;
//檢索字符串值
auto cpp_string = j_string.get <std :: string>();
//檢索字符串值(變量已經存在時的替代方法)
std :: string cpp_string2;
j_string.get_to(cpp_string2);
//檢索序列化的值(顯式JSON序列化)
std :: string serialized_string = j_string.dump();
//輸出原始字符串
std :: cout << cpp_string << “ == ” << cpp_string2 << “ == ” << j_string.get <std :: string>()<< ' \ n ' ;
//輸出序列化值
std :: cout << j_string << “ == ” << serialized_string << std :: endl;
.dump()始終返回序列化的值,並.get<std::string>()返回原始存儲的字符串值。
請注意,該庫僅支持UTF-8。當在庫中存儲具有不同編碼的字符串時,dump()除非json::error_handler_t::replace或json::error_handler_t::ignore用作錯誤處理程序,否則調用可能會引發異常。
往返流(例如文件,字符串流)
還可以使用流來序列化和反序列化:
//從標准輸入反序列化
json j;
std :: cin >> j;
//序列化為標准輸出
std :: cout << j;
// setw操縱器過載以設置漂亮打印的縮進
std :: cout << std :: setw( 4)<< j << std :: endl;
這些運算符可用於std::istream或的任何子類std::ostream。這是文件的相同示例:
//讀取JSON文件
std :: ifstream i( “ file.json ”);
json j;
>> j;
//將經過修飾的JSON寫入另一個文件
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);
自定義數據源
由於parse函數接受任意迭代器范圍,因此可以通過實現此LegacyInputIterator概念來提供自己的數據源。
struct MyContainer {
void advance();
const char&get_current();
};
struct MyIterator {
使用 difference_type = std :: ptrdiff_t ;
使用 value_type = char ;
使用指針= const char *;
使用 reference = const char&;
使用 iterator_category = std :: input_iterator_tag;
MyIterator&運算符 ++(){
MyContainer。前進();
返回 * 這個 ;
}
布爾 運算符!=(const MyIterator&rhs)const {
return rhs。目標!=目標;
}
引用運算符 *()const {
返回目標。get_current();
}
MyContainer * target = nullptr ;
};
MyIterator 開始(MyContainer&tgt){
return MyIterator {&tgt};
}
MyIterator 結束(const MyContainer&){
return {};
}
無效 foo(){
MyContainer c;
json j = json :: parse(c);
}
SAX接口
該庫使用具有以下功能的類SAX接口:
//當解析為
null 時調用bool null();
//在解析布爾值時調用;值傳遞給
布爾布爾 值(布爾值);
//在解析有符號或無符號整數時調用;值傳遞給
bool number_integer( number_integer_t val);
bool number_unsigned( number_unsigned_t val);
//在解析浮點數時調用;值和原始字符串傳遞給
bool number_float( number_float_t val, const string_t&s);
//解析字符串時調用;值被傳遞並且可以安全地移開
布爾 字符串( string_t&val);
//分別在對象或數組開始或結束時調用。傳遞的元素數量(如果不知道,則傳遞-1)
bool start_object(std :: size_t元素);
bool end_object();
bool start_array(std :: size_t元素);
bool end_array();
//在解析對象鍵時調用;值被傳遞並且可以安全地移開
bool 鍵( string_t&val);
//發生解析錯誤時調用;字節位置,最后一個標記和一個異常被傳遞給
bool parse_error(std :: size_t position, const std :: string&last_token, const detail :: exception&ex);
每個函數的返回值確定是否應該進行解析。
要實現自己的SAX處理程序,請按照下列步驟操作:
- 在一個類中實現SAX接口。可以將類nlohmann::json_sax<json>用作基類,但也可以使用實現了上述功能並且將其公開的任何類。
- 創建SAX接口類的對象,例如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),請參見file json_sax.hpp。
類似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);
//
為(json :: iterator it = j.begin(); it!= j.end(); ++ it)迭代數組 {
std :: cout << * it << ' \ n ' ;
}
//基於范圍的
for( auto&element:j){
std :: cout <<元素<< ' \ n ' ;
}
// getter / setter
const auto tmp = j [ 0 ] .get <std :: string>();
j [ 1 ] = 42 ;
bool foo = j.at(2);
//比較
j == “ [ \” foo \“,42,true] ” _json; //正確
//其東西
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 o;
o [ “ foo ” ] = 23 ;
o [ “ bar ” ] = false ;
o [ “ baz ” ] = 3.141 ;
//還可以使用
emplace o.emplace( “ weather ”, “ sunny ”);
// //對象專用的迭代器成員函數,
用於(json :: iterator it = o.begin(); it!= o.end(); ++ it){
std :: cout <<。key()<< “:” <<。value()<< “ \ n ” ;
}
//與
for( auto&el:o.items()){
std :: cout << el。key()<< “:” << << value()<< “ \ n ” ;
}
//即使結構化綁定(C ++ 17)更容易
為(自動&[鍵,值]:o.items()){
std :: cout <<鍵<< “:” <<值<< “ \ n ” ;
}
//找到一個條目
,如果(o.contains( “富”)){
//存在與鍵“foo”的條目
}
//或者通過find和一個迭代器
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 ”);