《二、基礎方法》:節點訪問、值獲取、顯式 vs 隱式、異常處理、迭代器、類型檢測、異常處理……一節課搞定C++處理JSON數據85%的需求……
JSON 字段的簡單類型包括:number、boolean、string 和 null(即空值);復雜類型則有 對象(Object)和數組(Array)兩類。
1 節點與值
訪問對象的指定名字的某個字段,可以使用 [“key”] ,訪問指定下標的某個元素,可以使用 [ index ] 來訪問(二者本質都是對 [] 操作符的重載),也可以通過方法 at(key/index) 來訪問。
當指定名字(key)或下標(index)并無對應數據可供訪問時,在 nlohmann/json 中,都被視為越界操作;此時,使用 [ ] 將“喜提”未定義(UB)的結果,而使用 at () 則得到異常。
通過 [] 或 at() 得到是 JSON 節點 (一個字段,或一個數組的一個元素);更多的時候,我們想要得到既定類型的值。
假設有這樣一個 nlohmann/json 對象:
nlohmann::json o =
{{ "id","ORD20250409-191" },{ "customerID", 10345 },{ "items", {123, 94320, 8} },{ "totalAmount", 172.8 },{ "orderDate","2025/04/09" }
};
存在隱式和顯式兩種取值方法,如下:
int id1 = o["customerID"]; // 隱式
int id2;
id2 = o["customerID"]; // 隱式int id3 = o["customerID"].get<int>(); // 顯式,適用定義新變量int id4;
o["customerID"].get_to(id4); // 顯式,類型信息來自已定義的 id4
這里的顯式或隱式,指的類型轉換過程:JSON 節點類型到目標類型的轉換過程。隱式轉換會在以下兩點都滿足時,出現問題(造成編譯失敗):
- 目標類型重載了賦值操作符(即: = );
- 轉換時,目標對象是已定義變量(即:確實在為某個“老”對象賦值,而非在構造新對象)。
如需進一步了解隱式轉換出錯的原因,建議到 d2school.com 對應課堂閱讀擴展內容。
nlohmann/json官方推薦使用 get() 或 get_to (…) 顯式指定要轉換的目標類型。如果需要的是更嚴格項目管理,可以在項目中定義全局宏:JSON_USE_IMPLICIT_CONVERSIONS=0
以禁用隱式取值,如是CMake項目,在CMakeList.txt 內添加代碼: SET (JSON_ImplicitConversions OFF)
,可得相同效果。
2 迭代器
借助迭代器,有四種 for 循環可用以迭代 JSON 對象的內容(假設 o 為 某json對象):
- 循環1
for (auto it = o.begin(); it != o.end(); ++it)
{cout << it.key() << ":" << it.value() << "\n";
}
- 循環2
for (auto it : o.items()) // 本質同循環1
{cout << it.key() << ":" << it.value() << "\n";
}
- 循環3
for (auto v : o) // 此時只有 value,因此比較適合遍歷數組節點
{cout << v << "\n";
}
- 循環4
for (auto & [k, v] : o.items()) // 需 c++17 結構化綁定支持
{cout << k << ":" << v << "\n";
}
3 異常
nlohmann/json 日常操作中,有三種常見異常類型(它們的基類都是 nlohmann::json::exception)。
- json::parse_error / 解析出錯
解析的數據中,存在格式(包括編碼)非法的數據,典型如:包含了非 UNICODE 編碼的漢字內容。nlohmann/json 支持的UNICODE編碼具體包括:UTF-8、UTF-16、UTF-32等。
注:“注釋”在 JSON 標準規范中,也是一種非常格式,但因為太常見,所以 nlohmann/json 提供了支持(非默認),詳見視頻一(快速認識)。
- json::out_of_range / 越界訪問
使用 at(key/index) 訪問數據時,查找無指定字段名或下標對應的數據時,即拋出該異常。
- json::type_error / 類型不匹配
典型如,對一個非對象、非數組類型的JSON節點,執行 push_back(新元素)
操作。
4 視頻2:基礎方法
010-nlohmann/json-2-基礎方法
5 示例項目-常用方法
- 報文 demo.json
{"name" : "丁小明","age" : 12
}
- 代碼
#include <cassert>#include <iostream>
#include <fstream>
#include <string>
#include <vector>#include <nlohmann/json.hpp>using json = nlohmann::json;int main()
{nlohmann::json o1 = {{ "id","ORD20250409-191" },{ "customerID", 10345 },{ "items", {123, 94320, 8} },{ "totalAmount", 172.8 },{ "orderDate","2025/04/09" }};std::cout << o1["id"] << std::endl;std::cout << o1["customerID"] << std::endl;std::cout << o1["items"] << std::endl;std::cout << o1["totalAmount"] << std::endl;std::cout << o1["orderDate"] << std::endl;auto node = o1["id"];std::cout << "node type-name is :\n" << typeid(node).name() << std::endl;// 隱式轉換類型,以獲取值{std::string id1 = o1["id"];int customerID = o1["customerID"];std::cout << id1 << "," << customerID << std::endl;}// 顯式轉換類型,以獲取值{auto id2 = o1["id"].get<std::string>();auto customerID2 = o1["customerID"].get<int>();std::cout << id2 << "," << customerID2 << std::endl;}{double totalAmount;o1["totalAmount"].get_to(totalAmount);std::cout << totalAmount << std::endl;std::cout << o1["totalAmount"].get_to(totalAmount) << std::endl; }// find、at {json o; o["name"] = "丁小明";o["age"] = 12;try{std::cout << o["Name"].get<std::string>() << " is "<< o["age"].get<int>() <<std::endl;}catch(std::exception const& e){std::cout << e.what() << std::endl;}auto it = o.find("Name1");if (it != o.end()){std::cout << it->get<std::string>() << std::endl;}else{std::cerr << "no found field : Name1." << std::endl;}try{std::cout << o.at("NAME").get<std::string>() << " is "<< o["age"].get<int>() <<std::endl;}catch(std::exception const& e){std::cout << e.what() << std::endl;} std::cout << o.dump(2) << std::endl;}// 迭代器、循環{for (auto const it : o1.items()){ std::cout << it.key() << " ==> " << it.value() << "\ttype : " << it.value().type_name() << std::endl;}std::cout << "==================\n";for (auto [k, v] : o1.items()){ std::cout << k << " ==> " << v << "\ttype : " << v.type_name() << std::endl;}o1["items"].push_back(999);std::cout << o1["items"] << std::endl;}// 異常: 非法JSON報文{std::string s = "\"Hello JSON!——第2學堂!\"";try{auto j = json::parse(s);std::cout << j.dump() << std::endl;}catch(json::parse_error const& e){std::cerr << e.id << "->" << e.what() << std::endl;} }// 從文件讀{// 請填寫你的 demo.json 的實際位置std::ifstream ifs ("D:\\...\\CommonlyUsedJSON\\demo.json");if (!ifs){std::cerr << "open file fail!" << std::endl;return -1;}try{std::cout << "== read from file : \n";auto j = json::parse(ifs);std::cout << j.dump(2) << std::endl;}catch(json::parse_error const& e){std::cerr << e.what() << std::endl;}}// 異常:嘗試和類型不匹配的行為{using namespace nlohmann::literals;json j = R"({"id" : "Hello!","items": [1, 2, 3]})"_json;try{j.at("items").push_back(4);j.at("id").push_back('a');}catch(json::type_error const& e){std::cerr << e.what() << std::endl;}}
}