在现代c++开发中,json(javascript object notation)已成为最流行的数据交换格式之一。无论是网络通信、配置文件读取还是数据持久化,json都扮演着重要角色。然而,c++标准库并没有提供原生的json支持,这就需要我们借助第三方库来处理json数据。
本文将详细介绍三种主流的c++ json库:jsoncpp、nlohmann/json和rapidjson,通过实际代码示例展示如何使用它们读取和解析json文件。
为什么需要json库?
json以其轻量级、易读易写的特性,在web api、配置文件、数据存储等场景中广泛应用。在c++中处理json数据时,我们需要解决以下问题:
- 解析json字符串或文件
- 验证json格式的正确性
- 访问和修改json数据
- 将c++数据结构序列化为json
下面让我们深入了解这三种库的具体用法。
1. jsoncpp - 经典稳定的选择
jsoncpp是一个历史悠久、稳定可靠的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;
}
// 创建json读取器
json::charreaderbuilder readerbuilder;
json::value root; // 存储解析后的 json 数据
std::string errs; // 存储错误信息
// 解析json
bool success = json::parsefromstream(readerbuilder, file, &root, &errs);
if (!success) {
std::cerr << "json解析错误: " << errs << std::endl;
return;
}
// 访问基本数据类型
if (root.ismember("name")) {
std::string name = root["name"].asstring(); //访问字符串类型
std::cout << "姓名: " << name << std::endl;
}
if (root.ismember("age")) {
int age = root["age"].asint(); //访问整数类型
std::cout << "年龄: " << age << std::endl;
}
if (root.ismember("is_student")) {
bool isstudent = root["is_student"].asbool(); //访问布尔类型
std::cout << "是否学生: " << (isstudent ? "是" : "否") << std::endl;
}
// 访问数组
if (root.ismember("hobbies") && root["hobbies"].isarray()) {
std::cout << "爱好: ";
//访问数组类型
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;
// 设置数组
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 - 现代易用的选择
nlohmann/json是一个单头文件的现代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);
// 访问基本数据类型 - 方式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;
// 访问基本数据类型 - 方式2:带默认值的安全访问
double height = data.value("height", 175.5);
std::cout << "身高: " << height << "cm" << std::endl;
// 访问数组
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 是数组类型" << std::endl;
}
// 更复杂的遍历
std::cout << "\n所有数据:" << 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) {
// 创建json对象 - 方式1:类似字典操作
json data;
data["name"] = "王五";
data["age"] = 28;
data["is_student"] = true;
data["height"] = 180.2;
// 创建数组
data["hobbies"] = {"音乐", "电影", "运动"};
// 创建嵌套对象
data["address"] = {
{"street", "解放路789号"},
{"city", "广州"},
{"postcode", "510000"}
};
// 添加复杂结构
data["scores"] = {
{"数学", 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函数
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, {"编程", "数学"}};
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 # 如果有安装到系统目录,则不需要特殊参数 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;
}
// 安全访问整数成员
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;
}
// 访问数组
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);
// 添加数字成员
document.addmember("age", 22, allocator);
document.addmember("height", 175.5, allocator);
// 添加布尔成员
document.addmember("is_student", true, allocator);
// 添加数组
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 |
|---|---|---|---|
| 学习曲线 | 平缓 | 非常平缓 | 陡峭 |
| 代码简洁性 | 中等 | 最优 | 繁琐 |
| 类型安全 | 中等 | 优秀 | 需要手动检查 |
| 错误处理 | 返回bool | 异常机制 | 返回错误码 |
到此这篇关于c++读取json文件的三种方式小结(jsoncpp、nlohmann/json和rapidjson)的文章就介绍到这了,更多相关c++读取json内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论