C++讀取JSON文件的三種方式小結(jié)(jsoncpp、nlohmann/json和RapidJSON)
在現(xiàn)代C++開發(fā)中,JSON(JavaScript Object Notation)已成為最流行的數(shù)據(jù)交換格式之一。無論是網(wǎng)絡(luò)通信、配置文件讀取還是數(shù)據(jù)持久化,JSON都扮演著重要角色。然而,C++標(biāo)準(zhǔn)庫并沒有提供原生的JSON支持,這就需要我們借助第三方庫來處理JSON數(shù)據(jù)。
本文將詳細(xì)介紹三種主流的C++ JSON庫:jsoncpp、nlohmann/json和RapidJSON,通過實(shí)際代碼示例展示如何使用它們讀取和解析JSON文件。
為什么需要JSON庫?
JSON以其輕量級(jí)、易讀易寫的特性,在Web API、配置文件、數(shù)據(jù)存儲(chǔ)等場(chǎng)景中廣泛應(yīng)用。在C++中處理JSON數(shù)據(jù)時(shí),我們需要解決以下問題:
- 解析JSON字符串或文件
- 驗(yàn)證JSON格式的正確性
- 訪問和修改JSON數(shù)據(jù)
- 將C++數(shù)據(jù)結(jié)構(gòu)序列化為JSON
下面讓我們深入了解這三種庫的具體用法。
1. jsoncpp - 經(jīng)典穩(wěn)定的選擇
jsoncpp是一個(gè)歷史悠久、穩(wěn)定可靠的C++ JSON庫,被許多大型項(xiàng)目使用。
安裝與配置
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 << "錯(cuò)誤:無法打開文件 " << filename << std::endl;
return;
}
// 創(chuàng)建JSON讀取器
Json::CharReaderBuilder readerBuilder;
Json::Value root; // 存儲(chǔ)解析后的 JSON 數(shù)據(jù)
std::string errs; // 存儲(chǔ)錯(cuò)誤信息
// 解析JSON
bool success = Json::parseFromStream(readerBuilder, file, &root, &errs);
if (!success) {
std::cerr << "JSON解析錯(cuò)誤: " << 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 << "是否學(xué)生: " << (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;
}
// 訪問嵌套對(duì)象
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;
}
}
// 安全訪問,提供默認(rèn)值
double height = root.get("height", 170.0).asDouble();
std::cout << "身高: " << height << "cm" << std::endl;
}
void writeJsonFile(const std::string& filename) {
Json::Value root;
// 設(shè)置基本值
root["name"] = "李四";
root["age"] = 30;
root["is_student"] = false;
// 設(shè)置數(shù)組
Json::Value hobbies(Json::arrayValue);
hobbies.append("旅游");
hobbies.append("攝影");
hobbies.append("烹飪");
root["hobbies"] = hobbies;
// 設(shè)置嵌套對(duì)象
Json::Value address;
address["street"] = "中山路456號(hào)";
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是一個(gè)單頭文件的現(xiàn)代C++ JSON庫,以其簡(jiǎn)潔的API和強(qiáng)大的功能而聞名。
安裝與配置
單頭文件方式(推薦):
// 只需下載一個(gè)頭文件 #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 << "是否學(xué)生: " << (isStudent ? "是" : "否") << std::endl;
// 訪問基本數(shù)據(jù)類型 - 方式2:帶默認(rèn)值的安全訪問
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;
// 訪問嵌套對(duì)象
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;
}
// 更復(fù)雜的遍歷
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解析錯(cuò)誤: " << e.what() << std::endl;
} catch (const json::type_error& e) {
std::cerr << "類型錯(cuò)誤: " << e.what() << std::endl;
} catch (const std::exception& e) {
std::cerr << "錯(cuò)誤: " << e.what() << std::endl;
}
}
void writeJsonFile(const std::string& filename) {
// 創(chuàng)建JSON對(duì)象 - 方式1:類似字典操作
json data;
data["name"] = "王五";
data["age"] = 28;
data["is_student"] = true;
data["height"] = 180.2;
// 創(chuàng)建數(shù)組
data["hobbies"] = {"音樂", "電影", "運(yùn)動(dòng)"};
// 創(chuàng)建嵌套對(duì)象
data["address"] = {
{"street", "解放路789號(hào)"},
{"city", "廣州"},
{"postcode", "510000"}
};
// 添加復(fù)雜結(jié)構(gòu)
data["scores"] = {
{"數(shù)學(xué)", 95},
{"英語", 88},
{"物理", 92}
};
// 寫入文件,美化輸出
std::ofstream file(filename);
if (file.is_open()) {
file << data.dump(4); // 縮進(jìn)4個(gè)空格
std::cout << "JSON文件已寫入: " << filename << std::endl;
}
}
// 高級(jí)功能:C++類型與JSON自動(dòng)轉(zhuǎn)換
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自動(dòng)轉(zhuǎn)換到C++結(jié)構(gòu)體
json j = R"({
"name": "趙六",
"age": 35,
"is_student": false,
"hobbies": ["讀書", "寫作"]
})"_json;
Person person = j.get<Person>();
std::cout << "自定義類型姓名: " << person.name << std::endl;
// 從C++結(jié)構(gòu)體自動(dòng)轉(zhuǎn)換到JSON
Person newPerson{"錢七", 40, true, {"編程", "數(shù)學(xué)"}};
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專注于高性能和低內(nèi)存占用,特別適合對(duì)性能要求嚴(yán)格的場(chǎng)景。
安裝與配置
源碼集成:
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 << "錯(cuò)誤:無法打開文件 " << filename << std::endl;
return;
}
// 使用流包裝器讀取JSON
rapidjson::IStreamWrapper isw(ifs);
rapidjson::Document document;
// 解析JSON
document.ParseStream(isw);
// 檢查解析錯(cuò)誤
if (document.HasParseError()) {
std::cerr << "解析錯(cuò)誤 (offset " << document.GetErrorOffset()
<< "): " << rapidjson::GetParseError_En(document.GetParseError())
<< std::endl;
return;
}
// 檢查根對(duì)象類型
if (!document.IsObject()) {
std::cerr << "錯(cuò)誤:根元素不是對(duì)象" << 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 << "是否學(xué)生: " << (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;
}
// 訪問嵌套對(duì)象
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;
}
}
// 處理可能不存在的字段(帶默認(rèn)值)
double height = 170.0; // 默認(rèn)值
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);
// 添加嵌套對(duì)象
rapidjson::Value address(rapidjson::kObjectType);
rapidjson::Value street, city;
street.SetString("和平路101號(hào)", 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;
}
}
// 高級(jí)用法:使用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
三種庫的詳細(xì)對(duì)比
為了幫助您選擇合適的JSON庫,下面從多個(gè)維度進(jìn)行詳細(xì)對(duì)比:
性能對(duì)比
| 測(cè)試場(chǎng)景 | jsoncpp | nlohmann/json | RapidJSON |
|---|---|---|---|
| 解析速度 | 中等 | 較慢 | 最快 |
| 內(nèi)存占用 | 中等 | 較高 | 最低 |
| 序列化速度 | 中等 | 較慢 | 最快 |
API易用性對(duì)比
| 特性 | jsoncpp | nlohmann/json | RapidJSON |
|---|---|---|---|
| 學(xué)習(xí)曲線 | 平緩 | 非常平緩 | 陡峭 |
| 代碼簡(jiǎn)潔性 | 中等 | 最優(yōu) | 繁瑣 |
| 類型安全 | 中等 | 優(yōu)秀 | 需要手動(dòng)檢查 |
| 錯(cuò)誤處理 | 返回bool | 異常機(jī)制 | 返回錯(cuò)誤碼 |
到此這篇關(guān)于C++讀取JSON文件的三種方式小結(jié)(jsoncpp、nlohmann/json和RapidJSON)的文章就介紹到這了,更多相關(guān)C++讀取JSON內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Pipes實(shí)現(xiàn)LeetCode(193.驗(yàn)證電話號(hào)碼)
這篇文章主要介紹了Pipes實(shí)現(xiàn)LeetCode(193.驗(yàn)證電話號(hào)碼),本篇文章通過簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-08-08
C++微服務(wù)UserServer設(shè)計(jì)與實(shí)現(xiàn)方法詳解
在當(dāng)今云原生時(shí)代,微服務(wù)架構(gòu)已成為構(gòu)建大規(guī)模分布式系統(tǒng)的主流選擇,下面這篇文章主要介紹了C++微服務(wù)UserServer設(shè)計(jì)與實(shí)現(xiàn)的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2025-11-11
C/C++?Qt數(shù)據(jù)庫與SqlTableModel組件應(yīng)用教程
SqlTableModel?組件可以將數(shù)據(jù)庫中的特定字段動(dòng)態(tài)顯示在TableView表格組件中,這篇文章將主要介紹SqlTableModel組件一些常用的操作,需要的朋友可以參考一下2021-12-12
vs2019創(chuàng)建WebService服務(wù)的實(shí)現(xiàn)
這篇文章主要介紹了vs2019創(chuàng)建WebService服務(wù)的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-03-03
C++?primer超詳細(xì)講解關(guān)聯(lián)容器
兩個(gè)主要的關(guān)聯(lián)容器為map和set,map中元素是一些關(guān)鍵字—值對(duì),關(guān)鍵字起索引的作用,值則表示與索引相關(guān)聯(lián)的數(shù)據(jù)。set中每個(gè)元素只包含一個(gè)關(guān)鍵字,set支持高效的關(guān)鍵字查詢操作——檢查一個(gè)給定關(guān)鍵字是否在set中2022-07-07
C語言實(shí)現(xiàn)學(xué)生獎(jiǎng)學(xué)金評(píng)定系統(tǒng)
這篇文章主要介紹了C語言實(shí)現(xiàn)學(xué)生獎(jiǎng)學(xué)金評(píng)定系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-03-03

