C++讀取JSON文件的三種方式小結(jsoncpp、nlohmann/json和RapidJSON)
在現(xiàn)代C++開發(fā)中,JSON(JavaScript Object Notation)已成為最流行的數(shù)據(jù)交換格式之一。無論是網(wǎng)絡通信、配置文件讀取還是數(shù)據(jù)持久化,JSON都扮演著重要角色。然而,C++標準庫并沒有提供原生的JSON支持,這就需要我們借助第三方庫來處理JSON數(shù)據(jù)。
本文將詳細介紹三種主流的C++ JSON庫:jsoncpp、nlohmann/json和RapidJSON,通過實際代碼示例展示如何使用它們讀取和解析JSON文件。
為什么需要JSON庫?
JSON以其輕量級、易讀易寫的特性,在Web API、配置文件、數(shù)據(jù)存儲等場景中廣泛應用。在C++中處理JSON數(shù)據(jù)時,我們需要解決以下問題:
- 解析JSON字符串或文件
- 驗證JSON格式的正確性
- 訪問和修改JSON數(shù)據(jù)
- 將C++數(shù)據(jù)結構序列化為JSON
下面讓我們深入了解這三種庫的具體用法。
1. jsoncpp - 經(jīng)典穩(wěn)定的選擇
jsoncpp是一個歷史悠久、穩(wěn)定可靠的C++ JSON庫,被許多大型項目使用。
安裝與配置
Ubuntu/Debian:
sudo apt-get install libjsoncpp-dev
源碼編譯:
git clone https://github.com/open-source-parsers/jsoncpp.git cd jsoncpp mkdir build && cd build cmake .. && make && sudo make install
基本使用方法
#include <json/json.h>
#include <fstream>
#include <iostream>
#include <vector>
class JsonCppExample {
public:
void readJsonFile(const std::string& filename) {
// 打開文件
std::ifstream file(filename);
if (!file.is_open()) {
std::cerr << "錯誤:無法打開文件 " << filename << std::endl;
return;
}
// 創(chuàng)建JSON讀取器
Json::CharReaderBuilder readerBuilder;
Json::Value root; // 存儲解析后的 JSON 數(shù)據(jù)
std::string errs; // 存儲錯誤信息
// 解析JSON
bool success = Json::parseFromStream(readerBuilder, file, &root, &errs);
if (!success) {
std::cerr << "JSON解析錯誤: " << errs << std::endl;
return;
}
// 訪問基本數(shù)據(jù)類型
if (root.isMember("name")) {
std::string name = root["name"].asString(); //訪問字符串類型
std::cout << "姓名: " << name << std::endl;
}
if (root.isMember("age")) {
int age = root["age"].asInt(); //訪問整數(shù)類型
std::cout << "年齡: " << age << std::endl;
}
if (root.isMember("is_student")) {
bool isStudent = root["is_student"].asBool(); //訪問布爾類型
std::cout << "是否學生: " << (isStudent ? "是" : "否") << std::endl;
}
// 訪問數(shù)組
if (root.isMember("hobbies") && root["hobbies"].isArray()) {
std::cout << "愛好: ";
//訪問數(shù)組類型
for (const auto& hobby : root["hobbies"]) {
std::cout << hobby.asString() << " ";
}
std::cout << std::endl;
}
// 訪問嵌套對象
if (root.isMember("address")) {
Json::Value address = root["address"];
if (address.isMember("street")) {
std::cout << "街道: " << address["street"].asString() << std::endl;
}
if (address.isMember("city")) {
std::cout << "城市: " << address["city"].asString() << std::endl;
}
}
// 安全訪問,提供默認值
double height = root.get("height", 170.0).asDouble();
std::cout << "身高: " << height << "cm" << std::endl;
}
void writeJsonFile(const std::string& filename) {
Json::Value root;
// 設置基本值
root["name"] = "李四";
root["age"] = 30;
root["is_student"] = false;
// 設置數(shù)組
Json::Value hobbies(Json::arrayValue);
hobbies.append("旅游");
hobbies.append("攝影");
hobbies.append("烹飪");
root["hobbies"] = hobbies;
// 設置嵌套對象
Json::Value address;
address["street"] = "中山路456號";
address["city"] = "上海";
root["address"] = address;
// 寫入文件
std::ofstream file(filename);
if (file.is_open()) {
Json::StreamWriterBuilder writerBuilder;
std::unique_ptr<Json::StreamWriter> writer(writerBuilder.newStreamWriter());
writer->write(root, &file);
file.close();
std::cout << "JSON文件已寫入: " << filename << std::endl;
}
}
};
// 使用示例
int main() {
JsonCppExample example;
example.writeJsonFile("output.json");
example.readJsonFile("data.json");
return 0;
}
2. nlohmann/json - 現(xiàn)代易用的選擇
nlohmann/json是一個單頭文件的現(xiàn)代C++ JSON庫,以其簡潔的API和強大的功能而聞名。
安裝與配置
單頭文件方式(推薦):
// 只需下載一個頭文件 #include "nlohmann/json.hpp"
包管理器安裝:
# vcpkg vcpkg install nlohmann-json # Conan conan install nlohmann_json/3.11.2
基本使用方法
#include <nlohmann/json.hpp>
#include <fstream>
#include <iostream>
#include <vector>
using json = nlohmann::json;
class NlohmannJsonExample {
public:
void readJsonFile(const std::string& filename) {
try {
// 直接從文件讀取JSON
std::ifstream file(filename);
if (!file.is_open()) {
throw std::runtime_error("無法打開文件: " + filename);
}
json data = json::parse(file);
// 訪問基本數(shù)據(jù)類型 - 方式1:直接訪問
std::string name = data["name"];
int age = data["age"];
bool isStudent = data["is_student"];
std::cout << "姓名: " << name << std::endl;
std::cout << "年齡: " << age << std::endl;
std::cout << "是否學生: " << (isStudent ? "是" : "否") << std::endl;
// 訪問基本數(shù)據(jù)類型 - 方式2:帶默認值的安全訪問
double height = data.value("height", 175.5);
std::cout << "身高: " << height << "cm" << std::endl;
// 訪問數(shù)組
std::cout << "愛好: ";
for (const auto& hobby : data["hobbies"]) {
std::cout << hobby.get<std::string>() << " ";
}
std::cout << std::endl;
// 訪問嵌套對象
if (data.contains("address")) {
auto& address = data["address"];
std::cout << "地址: " << address["city"] << ", " << address["street"] << std::endl;
}
// 類型檢查和異常安全
if (data["hobbies"].is_array()) {
std::cout << "hobbies 是數(shù)組類型" << std::endl;
}
// 更復雜的遍歷
std::cout << "\n所有數(shù)據(jù):" << std::endl;
for (auto& [key, value] : data.items()) {
std::cout << key << ": " << value << std::endl;
}
} catch (const json::parse_error& e) {
std::cerr << "JSON解析錯誤: " << e.what() << std::endl;
} catch (const json::type_error& e) {
std::cerr << "類型錯誤: " << e.what() << std::endl;
} catch (const std::exception& e) {
std::cerr << "錯誤: " << e.what() << std::endl;
}
}
void writeJsonFile(const std::string& filename) {
// 創(chuàng)建JSON對象 - 方式1:類似字典操作
json data;
data["name"] = "王五";
data["age"] = 28;
data["is_student"] = true;
data["height"] = 180.2;
// 創(chuàng)建數(shù)組
data["hobbies"] = {"音樂", "電影", "運動"};
// 創(chuàng)建嵌套對象
data["address"] = {
{"street", "解放路789號"},
{"city", "廣州"},
{"postcode", "510000"}
};
// 添加復雜結構
data["scores"] = {
{"數(shù)學", 95},
{"英語", 88},
{"物理", 92}
};
// 寫入文件,美化輸出
std::ofstream file(filename);
if (file.is_open()) {
file << data.dump(4); // 縮進4個空格
std::cout << "JSON文件已寫入: " << filename << std::endl;
}
}
// 高級功能:C++類型與JSON自動轉換
struct Person {
std::string name;
int age;
bool is_student;
std::vector<std::string> hobbies;
// 必須提供to_json和from_json函數(shù)
NLOHMANN_DEFINE_TYPE_INTRUSIVE(Person, name, age, is_student, hobbies)
};
void useWithCustomType() {
// 從JSON自動轉換到C++結構體
json j = R"({
"name": "趙六",
"age": 35,
"is_student": false,
"hobbies": ["讀書", "寫作"]
})"_json;
Person person = j.get<Person>();
std::cout << "自定義類型姓名: " << person.name << std::endl;
// 從C++結構體自動轉換到JSON
Person newPerson{"錢七", 40, true, {"編程", "數(shù)學"}};
json newJson = newPerson;
std::cout << "生成的JSON: " << newJson.dump(2) << std::endl;
}
};
// 使用示例
int main() {
NlohmannJsonExample example;
example.writeJsonFile("nlohmann_output.json");
example.readJsonFile("data.json");
example.useWithCustomType();
return 0;
}
編譯命令
# 單頭文件方式,只需包含路徑 g++ -o nlohmann_example nlohmann_example.cpp -std=c++17 # 如果有安裝到系統(tǒng)目錄,則不需要特殊參數(shù) g++ -o nlohmann_example nlohmann_example.cpp
3. RapidJSON - 高性能的選擇
RapidJSON專注于高性能和低內存占用,特別適合對性能要求嚴格的場景。
安裝與配置
源碼集成:
git clone https://github.com/Tencent/rapidjson.git
包管理器:
# vcpkg vcpkg install rapidjson
基本使用方法
#include "rapidjson/document.h"
#include "rapidjson/istreamwrapper.h"
#include "rapidjson/ostreamwrapper.h"
#include "rapidjson/writer.h"
#include "rapidjson/stringbuffer.h"
#include <fstream>
#include <iostream>
#include <vector>
class RapidJsonExample {
public:
void readJsonFile(const std::string& filename) {
// 打開文件
std::ifstream ifs(filename);
if (!ifs.is_open()) {
std::cerr << "錯誤:無法打開文件 " << filename << std::endl;
return;
}
// 使用流包裝器讀取JSON
rapidjson::IStreamWrapper isw(ifs);
rapidjson::Document document;
// 解析JSON
document.ParseStream(isw);
// 檢查解析錯誤
if (document.HasParseError()) {
std::cerr << "解析錯誤 (offset " << document.GetErrorOffset()
<< "): " << rapidjson::GetParseError_En(document.GetParseError())
<< std::endl;
return;
}
// 檢查根對象類型
if (!document.IsObject()) {
std::cerr << "錯誤:根元素不是對象" << std::endl;
return;
}
// 安全訪問字符串成員
if (document.HasMember("name") && document["name"].IsString()) {
std::string name = document["name"].GetString();
std::cout << "姓名: " << name << std::endl;
}
// 安全訪問整數(shù)成員
if (document.HasMember("age") && document["age"].IsInt()) {
int age = document["age"].GetInt();
std::cout << "年齡: " << age << std::endl;
}
// 安全訪問布爾成員
if (document.HasMember("is_student") && document["is_student"].IsBool()) {
bool isStudent = document["is_student"].GetBool();
std::cout << "是否學生: " << (isStudent ? "是" : "否") << std::endl;
}
// 訪問數(shù)組
if (document.HasMember("hobbies") && document["hobbies"].IsArray()) {
const rapidjson::Value& hobbies = document["hobbies"];
std::cout << "愛好: ";
for (rapidjson::SizeType i = 0; i < hobbies.Size(); i++) {
if (hobbies[i].IsString()) {
std::cout << hobbies[i].GetString() << " ";
}
}
std::cout << std::endl;
}
// 訪問嵌套對象
if (document.HasMember("address") && document["address"].IsObject()) {
const rapidjson::Value& address = document["address"];
if (address.HasMember("street") && address["street"].IsString()) {
std::cout << "街道: " << address["street"].GetString() << std::endl;
}
if (address.HasMember("city") && address["city"].IsString()) {
std::cout << "城市: " << address["city"].GetString() << std::endl;
}
}
// 處理可能不存在的字段(帶默認值)
double height = 170.0; // 默認值
if (document.HasMember("height") && document["height"].IsDouble()) {
height = document["height"].GetDouble();
}
std::cout << "身高: " << height << "cm" << std::endl;
}
void writeJsonFile(const std::string& filename) {
rapidjson::Document document;
document.SetObject();
rapidjson::Document::AllocatorType& allocator = document.GetAllocator();
// 添加字符串成員
rapidjson::Value name;
name.SetString("孫八", allocator);
document.AddMember("name", name, allocator);
// 添加數(shù)字成員
document.AddMember("age", 22, allocator);
document.AddMember("height", 175.5, allocator);
// 添加布爾成員
document.AddMember("is_student", true, allocator);
// 添加數(shù)組
rapidjson::Value hobbies(rapidjson::kArrayType);
rapidjson::Value hobby1, hobby2, hobby3;
hobby1.SetString("游戲", allocator);
hobby2.SetString("編程", allocator);
hobby3.SetString("音樂", allocator);
hobbies.PushBack(hobby1, allocator);
hobbies.PushBack(hobby2, allocator);
hobbies.PushBack(hobby3, allocator);
document.AddMember("hobbies", hobbies, allocator);
// 添加嵌套對象
rapidjson::Value address(rapidjson::kObjectType);
rapidjson::Value street, city;
street.SetString("和平路101號", allocator);
city.SetString("深圳", allocator);
address.AddMember("street", street, allocator);
address.AddMember("city", city, allocator);
document.AddMember("address", address, allocator);
// 寫入文件
std::ofstream ofs(filename);
if (ofs.is_open()) {
rapidjson::OStreamWrapper osw(ofs);
rapidjson::Writer<rapidjson::OStreamWrapper> writer(osw);
document.Accept(writer);
std::cout << "JSON文件已寫入: " << filename << std::endl;
}
}
// 高級用法:使用StringBuffer
void useStringBuffer() {
rapidjson::Document document;
document.SetObject();
rapidjson::Document::AllocatorType& allocator = document.GetAllocator();
document.AddMember("message", "Hello, RapidJSON!", allocator);
document.AddMember("value", 42, allocator);
// 使用StringBuffer生成JSON字符串
rapidjson::StringBuffer buffer;
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
document.Accept(writer);
std::cout << "生成的JSON字符串: " << buffer.GetString() << std::endl;
}
};
// 使用示例
int main() {
RapidJsonExample example;
example.writeJsonFile("rapidjson_output.json");
example.readJsonFile("data.json");
example.useStringBuffer();
return 0;
}
編譯命令
g++ -o rapidjson_example rapidjson_example.cpp -I/path/to/rapidjson/include
三種庫的詳細對比
為了幫助您選擇合適的JSON庫,下面從多個維度進行詳細對比:
性能對比
| 測試場景 | jsoncpp | nlohmann/json | RapidJSON |
|---|---|---|---|
| 解析速度 | 中等 | 較慢 | 最快 |
| 內存占用 | 中等 | 較高 | 最低 |
| 序列化速度 | 中等 | 較慢 | 最快 |
API易用性對比
| 特性 | jsoncpp | nlohmann/json | RapidJSON |
|---|---|---|---|
| 學習曲線 | 平緩 | 非常平緩 | 陡峭 |
| 代碼簡潔性 | 中等 | 最優(yōu) | 繁瑣 |
| 類型安全 | 中等 | 優(yōu)秀 | 需要手動檢查 |
| 錯誤處理 | 返回bool | 異常機制 | 返回錯誤碼 |
到此這篇關于C++讀取JSON文件的三種方式小結(jsoncpp、nlohmann/json和RapidJSON)的文章就介紹到這了,更多相關C++讀取JSON內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
基于大端法、小端法以及網(wǎng)絡字節(jié)序的深入理解
本篇文章是對大端法、小端法以及網(wǎng)絡字節(jié)序進行了詳細的分析介紹,需要的朋友參考下2013-05-05
C++/類與對象/默認成員函數(shù)@構造函數(shù)的用法
這篇文章主要介紹了C++/類與對象/默認成員函數(shù)@構造函數(shù)的用法,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2025-06-06

