jsoncpp
- 1. JSON數據
- 1.1 JSON 的基本語法規則
- 1. 基礎語法要求
- 兩種核心數據結構
- JSON 與其他數據格式的對比
- 1.2 JSON 的典型應用場景
- 1.3 JSON 解析與生成工具
- 2. 編程語言庫(解析/生成)
- 1.4 常見錯誤與注意事項
- 2. jsoncpp
- 2.1 基本用法
- 1. 安裝與集成
- 2. 核心類與命名空間
- 3. 解析 JSON 字符串**
- 4. 生成 JSON 字符串
- 5. 讀寫 JSON 文件
- 2.2 主要類和函數
- 核心類
- 1. `Json::Value`
- 2. `Json::Reader`(舊版解析器)
- 3. `Json::CharReader`(新版解析器)
- 4. `Json::Writer` 及其派生類
- 5. `Json::CharReaderBuilder` 與 `Json::StreamWriterBuilder`
- 輔助函數與工具
- 1. 類型轉換函數
- 2. 文件操作輔助
- 核心類關系與工作流程
- 總結
- 2.3 高級特性
- 2.4 總結
- 2.5 示例
1. JSON數據
JSON(JavaScript Object Notation,JavaScript 對象表示法)是一種輕量級、文本格式的通用數據交換格式,旨在簡化數據的存儲、傳輸和解析。它基于 JavaScript 的對象語法,但獨立于編程語言,幾乎所有主流語言(如 C++、Python、Java、JavaScript)都提供原生或第三方庫支持。
- 簡潔易讀:采用人類可直接理解的文本結構,比 XML 更簡潔(無冗余標簽)。
- 跨平臺/跨語言:不依賴特定編程語言,是不同系統間數據交換的“通用語言”(如前后端通信、服務間調用)。
- 輕量級:相同數據的 JSON 體積遠小于 XML,傳輸效率更高。
- 支持常用數據類型:覆蓋字符串、數字、布爾、數組、對象等基礎數據結構,滿足大多數場景需求。
1.1 JSON 的基本語法規則
JSON 數據由兩種核心結構組成:鍵值對集合(對象) 和 有序值列表(數組),所有語法需嚴格遵循以下規則:
1. 基礎語法要求
- 鍵名必須用 雙引號
"
包裹(不能用單引號或無引號)。 - 值可以是字符串、數字、布爾、
null
、對象或數組。 - 鍵值對之間用 逗號
,
分隔(最后一個鍵值對后不能加逗號,否則會解析錯誤)。 - 字符串必須用雙引號包裹,支持轉義字符(如
\n
換行、\"
雙引號、\\
反斜杠)。
兩種核心數據結構
- 對象(Object):鍵值對的集合
用于表示“實體”(如用戶、配置項),格式為{ "鍵1": 值1, "鍵2": 值2, ... }
。
示例(用戶信息):
{"name": "Alice", // 字符串值"age": 25, // 數字值(整數/浮點數均可,無需引號)"isStudent": false, // 布爾值(true/false,無引號)"email": null, // 空值(null,無引號)"address": { // 嵌套對象(值為另一個 JSON 對象)"city": "Beijing","street": "Main Street","zipCode": "100000"}
}
- 數組(Array):有序的值列表
用于表示“多個同類型/相關值的集合”(如列表、數組),格式為[ 值1, 值2, ... ]
。
示例(用戶列表/愛好):
// 數組作為頂層結構(用戶列表)
[{ "name": "Alice", "age": 25 },{ "name": "Bob", "age": 30 }
]// 數組作為對象的值(用戶愛好)
{"name": "Charlie","hobbies": ["reading", "coding", "hiking"] // 字符串數組
}
- 常見數據類型及示例
數據類型 | 語法規則 | 示例 |
---|---|---|
字符串 | 雙引號包裹,支持轉義 | "name": "Alice" , "intro": "I'm a developer\nLove coding" |
數字 | 整數/浮點數,無引號,支持負數和科學計數法 | "age": 25 , "score": 98.5 , "value": -100 , "sci": 1e5 |
布爾 | 僅 true 或 false (無引號) | "isStudent": true |
null | 表示“無值”(無引號) | "email": null |
對象 | {} 包裹的鍵值對集合,可嵌套 | "address": { "city": "Shanghai" } |
數組 | [] 包裹的有序值列表,值類型可混合 | "tags": [1, "important", false] |
JSON 與其他數據格式的對比
格式 | 優點 | 缺點 | 適用場景 |
---|---|---|---|
JSON | 簡潔、輕量、跨語言支持好、易解析 | 不支持注釋(需通過工具擴展)、不適合復雜文檔結構 | 前后端通信、API 接口、配置文件、輕量級數據交換 |
XML | 支持注釋、命名空間、復雜文檔結構(如嵌套層級深的配置) | 冗余標簽多、體積大、解析效率低 | 傳統企業級應用(如 SOAP 接口)、配置文件(如 Spring) |
YAML | 語法更簡潔(無引號/括號,用縮進表示層級)、支持注釋 | 對縮進敏感(易因格式錯誤導致解析失敗) | 配置文件(如 Docker、K8s、Python 項目) |
1.2 JSON 的典型應用場景
-
前后端數據通信:前端(如 JavaScript)通過 AJAX/fetch 向后端(如 Java/C++)發送 JSON 請求,后端返回 JSON 響應(最常見場景)。
示例(后端接口響應):{"code": 200, // 狀態碼(200=成功,404=未找到)"message": "success","data": { // 業務數據"userList": [{"name": "Alice"}, {"name": "Bob"}],"total": 2} }
-
配置文件:用 JSON 存儲應用程序的配置(如窗口大小、數據庫連接信息),比 ini 文件支持更復雜的結構。
示例(應用配置):{"appName": "MyEditor","window": {"width": 1024,"height": 768,"fullscreen": false},"database": {"host": "localhost","port": 3306,"username": "root","password": "123456"} }
-
數據存儲:小型應用用 JSON 文件存儲數據(如用戶列表、日志),無需依賴數據庫。
示例(用戶數據文件users.json
):[{"id": 1, "name": "Alice", "age": 25},{"id": 2, "name": "Bob", "age": 30} ]
-
API 接口規范:絕大多數現代 API(如 RESTful API)采用 JSON 作為數據交換格式(如 GitHub API、微信支付 API)。
1.3 JSON 解析與生成工具
- 在線工具(驗證/格式化)
- JSON.cn:在線解析、格式化、驗證 JSON(中文友好)。
- JSON Validator:檢查 JSON 語法錯誤,格式化代碼。
2. 編程語言庫(解析/生成)
- C++:
jsoncpp
、nlohmann/json
(單頭文件,更簡潔)、rapidjson
(高性能)。 - Python:內置
json
模塊(無需額外安裝)。 - JavaScript:內置
JSON.parse()
(字符串轉對象)和JSON.stringify()
(對象轉字符串)。 - Java:
Gson
(Google)、Jackson
、Fastjson
(阿里)。
1.4 常見錯誤與注意事項
- 鍵名未用雙引號:錯誤寫法
{ name: "Alice" }
→ 正確寫法{ "name": "Alice" }
。 - 多余的逗號:錯誤寫法
{ "name": "Alice", "age": 25, }
→ 需刪除最后一個逗號。 - 字符串用單引號:錯誤寫法
{ "name": 'Alice' }
→ 正確寫法{ "name": "Alice" }
。 - 布爾/數字加引號:錯誤寫法
{ "age": "25", "isStudent": "true" }
→ 正確寫法{ "age": 25, "isStudent": true }
(否則解析為字符串,而非對應類型)。 - 不支持注釋:JSON 標準不允許注釋,若需添加注釋,需用工具(如
jsonc
擴展)或在解析前手動刪除注釋。
JSON 是目前最流行的數據交換格式之一,核心優勢是簡潔、跨平臺、易解析。掌握其語法規則和應用場景,是開發中處理數據交換(如前后端通信、配置文件)的基礎。實際開發中,需注意語法規范性(避免解析錯誤),并根據語言選擇合適的庫(如 C++ 用 jsoncpp
,Python 用內置 json
模塊)實現 JSON 的解析與生成。
2. jsoncpp
jsoncpp 是一個輕量級的 C++ 庫,用于解析、生成和操作 JSON 數據。它支持 JSON 標準規范,提供了簡單易用的 API,廣泛應用于需要處理 JSON 格式數據的 C++ 項目中(如網絡通信、配置文件解析等)。
- JSON 解析:將 JSON 字符串或文件解析為 C++ 可操作的對象模型。
- JSON 生成:將 C++ 對象模型序列化為 JSON 字符串或寫入文件。
- 數據操作:支持增刪改查 JSON 中的鍵值對、數組元素等。
- 跨平臺:兼容 Windows、Linux、macOS 等主流操作系統。
2.1 基本用法
1. 安裝與集成
- 源碼集成
從 jsoncpp 官網 下載源碼,將include/json
目錄和src/lib_json
下的.cpp
文件添加到項目中。
jsoncpp庫的安裝、編譯和配置
2. 核心類與命名空間
Json::Value
:表示 JSON 中的任何值(對象、數組、字符串、數字等),是最常用的類。Json::Reader
:用于解析 JSON 字符串或文件到Json::Value
。Json::Writer
:用于將Json::Value
序列化為 JSON 字符串(派生類Json::FastWriter
、Json::StyledWriter
分別生成緊湊/格式化的字符串)。- 命名空間:所有類和函數都在
Json
命名空間下。
3. 解析 JSON 字符串**
#include <iostream>
#include <json/json.h>int main() {// JSON 字符串std::string json_str = R"({"name": "Alice","age": 25,"is_student": false,"hobbies": ["reading", "coding"]})";// 解析 JSONJson::Reader reader;Json::Value root;if (!reader.parse(json_str, root)) {std::cerr << "JSON 解析失敗: " << reader.getFormattedErrorMessages() << std::endl;return 1;}// 讀取數據std::string name = root["name"].asString();int age = root["age"].asInt();bool is_student = root["is_student"].asBool();std::cout << "姓名: " << name << std::endl;std::cout << "年齡: " << age << std::endl;std::cout << "是否學生: " << (is_student ? "是" : "否") << std::endl;// 讀取數組std::cout << "愛好: ";for (const auto& hobby : root["hobbies"]) {std::cout << hobby.asString() << " ";}std::cout << std::endl;return 0;
}
4. 生成 JSON 字符串
#include <json/json.h>
#include <iostream>int main() {// 創建 JSON 對象Json::Value root;// 添加鍵值對root["name"] = "Bob";root["age"] = 30;root["is_employee"] = true;// 添加數組Json::Value skills;skills.append("C++");skills.append("Python");skills.append("Java");root["skills"] = skills;// 生成格式化的 JSON 字符串(帶縮進)Json::StyledWriter styled_writer;std::string styled_str = styled_writer.write(root);std::cout << "格式化輸出:\n" << styled_str << std::endl;// 生成緊湊的 JSON 字符串(無縮進)Json::FastWriter fast_writer;std::string fast_str = fast_writer.write(root);std::cout << "緊湊輸出:\n" << fast_str << std::endl;return 0;
}
輸出結果:
格式化輸出:
{"age" : 30,"is_employee" : true,"name" : "Bob","skills" : [ "C++", "Python", "Java" ]
}緊湊輸出:
{"age":30,"is_employee":true,"name":"Bob","skills":["C++","Python","Java"]}
5. 讀寫 JSON 文件
#include <json/json.h>
#include <fstream>
#include <iostream>// 寫入 JSON 到文件
void writeJsonToFile(const std::string& filename) {Json::Value root;root["config"] = "app_settings";root["window"]["width"] = 800;root["window"]["height"] = 600;std::ofstream ofs(filename);if (ofs.is_open()) {Json::StyledWriter writer;ofs << writer.write(root);ofs.close();}
}// 從文件讀取 JSON
void readJsonFromFile(const std::string& filename) {std::ifstream ifs(filename);if (!ifs.is_open()) {std::cerr << "無法打開文件: " << filename << std::endl;return;}Json::Reader reader;Json::Value root;if (reader.parse(ifs, root)) {std::cout << "配置名稱: " << root["config"].asString() << std::endl;std::cout << "窗口寬度: " << root["window"]["width"].asInt() << std::endl;std::cout << "窗口高度: " << root["window"]["height"].asInt() << std::endl;} else {std::cerr << "文件解析失敗: " << reader.getFormattedErrorMessages() << std::endl;}ifs.close();
}int main() {std::string filename = "config.json";writeJsonToFile(filename);readJsonFromFile(filename);return 0;
}
2.2 主要類和函數
Jsoncpp 庫通過幾個核心類實現了 JSON 數據的解析、生成和操作,以下是其主要類、函數及其功能的詳細說明:
核心類
1. Json::Value
- 功能:表示 JSON 中的任何值(對象、數組、字符串、數字等),是 Jsoncpp 中最基礎、最常用的類。
- 特點:
- 可動態存儲不同類型的 JSON 數據(自動類型轉換)。
- 支持嵌套結構(對象中包含對象或數組)。
- 常用方法:
方法 功能 asString()
轉換為字符串(若類型不匹配,返回空字符串) asInt()
/asDouble()
轉換為整數/浮點數(類型不匹配返回 0) asBool()
轉換為布爾值(類型不匹配返回 false
)isString()
/isInt()
/isBool()
檢查當前值的類型 isMember(const std::string& key)
檢查對象中是否包含指定鍵 size()
返回數組或對象的元素數量 append(const Value& value)
向數組添加元素(僅對數組類型有效) clear()
清空當前值(對象/數組的元素會被刪除) - 示例:
Json::Value root; root["name"] = "Alice"; // 字符串 root["age"] = 25; // 整數 root["scores"].append(90); // 數組添加元素 root["address"]["city"] = "Beijing"; // 嵌套對象
2. Json::Reader
(舊版解析器)
- 功能:將 JSON 字符串或輸入流解析為
Json::Value
對象。 - 注意:Jsoncpp 1.0+ 推薦使用
Json::CharReader
替代(性能更好)。 - 常用方法:
方法 功能 parse(const std::string& json_str, Value& root)
解析 JSON 字符串到 root
parse(std::istream& is, Value& root)
解析輸入流(如文件流)到 root
getFormattedErrorMessages()
解析失敗時返回錯誤信息 - 示例:
Json::Reader reader; Json::Value root; std::string json_str = "{\"name\":\"Alice\"}"; if (reader.parse(json_str, root)) {std::string name = root["name"].asString(); } else {std::cerr << "解析錯誤: " << reader.getFormattedErrorMessages(); }
3. Json::CharReader
(新版解析器)
- 功能:替代
Json::Reader
,提供更高效的 JSON 解析,支持從字符串或流中解析。 - 使用方式:通過
Json::CharReaderBuilder
創建實例(工廠模式)。 - 常用方法:
方法 功能 parse(const char* begin, const char* end, Value* root, std::string* errors)
解析字符范圍 [begin, end)
到root
,錯誤信息存入errors
- 示例:
Json::CharReaderBuilder builder; std::unique_ptr<Json::CharReader> reader(builder.newCharReader()); Json::Value root; std::string errors; const std::string json_str = "{\"age\":25}";if (reader->parse(json_str.data(), json_str.data() + json_str.size(), &root, &errors)) {int age = root["age"].asInt(); } else {std::cerr << "解析錯誤: " << errors; }
4. Json::Writer
及其派生類
- 功能:將
Json::Value
對象序列化為 JSON 字符串。 - 派生類:
Json::FastWriter
:生成緊湊無格式的 JSON 字符串(無縮進、換行)。Json::StyledWriter
:生成格式化的 JSON 字符串(帶縮進、換行,可讀性強)。Json::StyledStreamWriter
:直接向輸出流(如文件)寫入格式化的 JSON。
- 常用方法:
方法 功能 write(const Value& root)
將 root
序列化為 JSON 字符串(FastWriter
/StyledWriter
)write(std::ostream& out, const Value& root)
向輸出流寫入 JSON 字符串( StyledStreamWriter
) - 示例:
Json::Value root; root["name"] = "Bob";// 緊湊輸出 Json::FastWriter fast_writer; std::string fast_str = fast_writer.write(root); // 結果: {"name":"Bob"}// 格式化輸出 Json::StyledWriter styled_writer; std::string styled_str = styled_writer.write(root); // 帶縮進的格式化字符串
5. Json::CharReaderBuilder
與 Json::StreamWriterBuilder
- 功能:分別用于創建
Json::CharReader
和Json::StreamWriter
的工廠類,支持自定義解析/生成選項(如縮進空格數、錯誤提示語言)。 - 示例(自定義格式化參數):
Json::StreamWriterBuilder builder; builder["indentation"] = " "; // 設置縮進為 2 個空格 std::unique_ptr<Json::StreamWriter> writer(builder.newStreamWriter());Json::Value root; root["name"] = "Charlie"; writer->write(root, &std::cout); // 向控制臺輸出格式化 JSON
輔助函數與工具
1. 類型轉換函數
Json::ValueType Json::Value::type()
:返回當前值的類型(nullValue
、intValue
、stringValue
等)。bool Json::Value::empty()
:檢查值是否為空(空對象、空數組或null
)。
2. 文件操作輔助
- 結合 C++ 標準流(
std::ifstream
/std::ofstream
)實現 JSON 文件的讀寫:// 從文件讀取 JSON std::ifstream ifs("data.json"); Json::Value root; ifs >> root; // 需包含 <json/json.h>,重載了流運算符// 寫入 JSON 到文件 std::ofstream ofs("output.json"); ofs << root;
核心類關系與工作流程
- 解析流程:
輸入(JSON 字符串/文件) →CharReader
(解析) →Value
(內存對象)。 - 生成流程:
Value
(內存對象) →Writer
(序列化) → 輸出(JSON 字符串/文件)。
總結
類/工具 | 核心功能 | 適用場景 |
---|---|---|
Json::Value | 存儲和操作 JSON 數據 | 所有需要處理 JSON 的場景(核心) |
Json::CharReader | 解析 JSON 為 Value | 從字符串/文件讀取 JSON |
Json::FastWriter | 生成緊湊 JSON 字符串 | 網絡傳輸(體積小) |
Json::StyledWriter | 生成格式化 JSON 字符串 | 配置文件(可讀性強) |
CharReaderBuilder /StreamWriterBuilder | 自定義解析/生成參數 | 需要定制 JSON 格式時 |
掌握這些核心類和方法后,即可完成 JSON 數據的解析、生成、修改等基本操作,滿足大多數 C++ 項目的 JSON 處理需求。
2.3 高級特性
- 類型檢查
使用isXxx()
方法檢查 JSON 值的類型,避免訪問錯誤類型導致崩潰:
if (root["age"].isInt()) {int age = root["age"].asInt();
} else {std::cerr << "age 不是整數類型" << std::endl;
}
- 處理可選鍵
使用isMember()
檢查鍵是否存在:
if (root.isMember("email")) {std::string email = root["email"].asString();
} else {std::cout << "email 鍵不存在" << std::endl;
}
- 嵌套對象與數組
支持多層嵌套的 JSON 結構:
// 嵌套對象
root["address"]["city"] = "Beijing";
root["address"]["street"] = "Main Street";// 數組操作
root["scores"].append(90);
root["scores"].append(85);
int first_score = root["scores"][0].asInt(); // 獲取第一個元素
2.4 總結
- 編碼問題:jsoncpp 默認處理 UTF-8 編碼,若輸入為其他編碼(如 GBK),需先轉換為 UTF-8。
- 性能考慮:
Json::Reader
解析大型 JSON 時可能較慢,可考慮使用更高效的解析器(如Json::CharReader
):Json::CharReaderBuilder builder; std::unique_ptr<Json::CharReader> reader(builder.newCharReader()); if (!reader->parse(json_str.data(), json_str.data() + json_str.size(), &root, nullptr)) {// 解析失敗 }
- 版本差異:jsoncpp 1.0 以上版本與舊版本(0.x)API 有差異,建議使用最新版本。
- 與其他庫對比
庫 | 特點 | 適用場景 |
---|---|---|
jsoncpp | 經典穩定、API 簡單、功能完整 | 大多數 C++ 項目,尤其是需要兼容性的場景 |
nlohmann/json | 單頭文件、現代 C++ 風格、易用性強 | 新項目,追求簡潔集成 |
rapidjson | 高性能、內存占用低 | 對性能要求高的場景(如服務器) |
jsoncpp 是一個成熟可靠的 JSON 處理庫,適合需要在 C++ 中解析或生成 JSON 數據的場景。其 API 設計直觀,易于上手,同時支持復雜的 JSON 結構操作。對于大多數項目,jsoncpp 能滿足基本需求;若追求更簡潔的集成方式,可考慮 nlohmann/json;若對性能有極致要求,可嘗試 rapidjson。
2.5 示例
#include <json/json.h>
#include <iostream>
#include <fstream>
#include <string>// 解析 JSON 字符串
void parseJsonString() {std::cout << "=== 解析 JSON 字符串 ===" << std::endl;std::string json_str = R"({"name": "Alice","age": 25,"is_student": false,"hobbies": ["reading", "coding"],"scores": {"math": 90,"english": 85}})";Json::CharReaderBuilder builder;std::unique_ptr<Json::CharReader> reader(builder.newCharReader());Json::Value root;std::string errors;if (reader->parse(json_str.data(), json_str.data() + json_str.size(), &root, &errors)) {std::cout << "姓名: " << root["name"].asString() << std::endl;std::cout << "年齡: " << root["age"].asInt() << std::endl;std::cout << "是否學生: " << (root["is_student"].asBool() ? "是" : "否") << std::endl;std::cout << "愛好: ";for (const auto& hobby : root["hobbies"]) {std::cout << hobby.asString() << " ";}std::cout << std::endl;std::cout << "數學成績: " << root["scores"]["math"].asInt() << std::endl;} else {std::cerr << "解析失敗: " << errors << std::endl;}
}// 生成 JSON 字符串
void generateJsonString() {std::cout << "\n=== 生成 JSON 字符串 ===" << std::endl;Json::Value root;root["name"] = "Bob";root["age"] = 30;root["is_employee"] = true;Json::Value skills;skills.append("C++");skills.append("Python");skills.append("Java");root["skills"] = skills;Json::Value projects;Json::Value project1;project1["name"] = "json_demo";project1["completed"] = true;projects.append(project1);root["projects"] = projects;// 格式化輸出Json::StyledWriter styled_writer;std::cout << "格式化 JSON:\n" << styled_writer.write(root) << std::endl;// 緊湊輸出Json::FastWriter fast_writer;std::cout << "緊湊 JSON:\n" << fast_writer.write(root) << std::endl;
}// 讀寫 JSON 文件
void readWriteJsonFile(const std::string& filename) {std::cout << "\n=== 讀寫 JSON 文件 ===" << std::endl;// 寫入文件Json::Value root;root["app_name"] = "MyApp";root["version"] = "1.0.0";root["settings"]["window_width"] = 1024;root["settings"]["window_height"] = 768;root["settings"]["fullscreen"] = false;std::ofstream ofs(filename);if (ofs.is_open()) {Json::StyledWriter writer;ofs << writer.write(root);ofs.close();std::cout << "已寫入文件: " << filename << std::endl;} else {std::cerr << "無法寫入文件: " << filename << std::endl;return;}// 讀取文件std::ifstream ifs(filename);if (ifs.is_open()) {Json::CharReaderBuilder builder;std::unique_ptr<Json::CharReader> reader(builder.newCharReader());Json::Value read_root;std::string errors;if (reader->parse(std::istreambuf_iterator<char>(ifs), std::istreambuf_iterator<char>(), &read_root, &errors)) {std::cout << "應用名稱: " << read_root["app_name"].asString() << std::endl;std::cout << "版本: " << read_root["version"].asString() << std::endl;std::cout << "窗口寬度: " << read_root["settings"]["window_width"].asInt() << std::endl;} else {std::cerr << "文件解析失敗: " << errors << std::endl;}ifs.close();} else {std::cerr << "無法讀取文件: " << filename << std::endl;}
}int main() {parseJsonString();generateJsonString();readWriteJsonFile("config.json");return 0;
}