C++ 使用 nlohmann/json 库解析和处理 JSON 数据的操作指南

nlohmann/json 是 C++ 中最流行的 JSON 处理库之一,以其简洁的 API、高性能和易用性著称。以下是详细的使用指南,涵盖从基础到进阶的操作。

图片[1]_C++ 使用 nlohmann/json 库解析和处理 JSON 数据的操作指南_知途无界

一、安装与引入

1. 安装方式

  • 直接包含头文件​(推荐):
    该库仅有一个头文件 json.hpp,下载后放入项目目录即可。 # 从 GitHub 下载最新版(如 v3.11.2) wget https://github.com/nlohmann/json/releases/download/v3.11.2/json.hpp
  • 包管理器安装​(如 vcpkg/conan): # vcpkg vcpkg install nlohmann-json # conan conan install nlohmann_json/3.11.2

2. 引入库

在代码中包含头文件:

#include <nlohmann/json.hpp>
using json = nlohmann::json; // 别名简化书写

二、基础操作

1. JSON 数据的创建

从字面量构造

// 空对象/数组
json j_obj{};                // {} (空对象)
json j_arr{};                // [] (空数组)

// 手动构造对象(键值对)
json person;
person["name"] = "Alice";
person["age"] = 25;
person["is_student"] = false;

// 直接初始化(推荐)
json j = {
    {"name", "Bob"},
    {"age", 30},
    {"hobbies", {"reading", "coding"}}  // 嵌套数组
};

从字符串解析

std::string json_str = R"({"name":"Charlie","score":95.5})";
json j = json::parse(json_str); // 解析字符串

// 从文件读取
std::ifstream file("data.json");
json j_file = json::parse(file); // 解析文件内容

2. 访问 JSON 数据

对象访问

json j = {{"name", "Alice"}, {"age", 25}};

// 方式1:[] 操作符(不检查键是否存在)
std::string name = j["name"]; // "Alice"
int age = j["age"];           // 25

// 方式2:.at() 方法(检查键是否存在,异常安全)
try {
    std::string name = j.at("name"); // 安全访问
    int invalid = j.at("invalid_key"); // 抛出 json::out_of_range 异常
} catch (const json::exception& e) {
    std::cerr << "Error: " << e.what() << std::endl;
}

// 检查键是否存在
if (j.contains("name")) {
    std::cout << "Name exists: " << j["name"] << std::endl;
}

数组访问

json arr = {"a", "b", 123};
std::string first = arr[0];  // "a" (索引从0开始)
int num = arr[2];            // 123

3. 修改 JSON 数据

json j = {{"name", "Alice"}, {"age", 25}};

// 修改值
j["age"] = 26;                // 更新 age
j["city"] = "New York";       // 新增键值对

// 修改数组
json arr = {1, 2, 3};
arr[1] = 99;                  // arr 变为 [1, 99, 3]
arr.push_back(4);             // arr 变为 [1, 99, 3, 4]

三、类型转换

1. JSON 与 C++ 类型互转

JSON → C++

json j = {
    {"name", "Bob"},
    {"age", 30},
    {"is_student", true},
    {"scores", {90, 85, 95}},
    {"metadata", nullptr}
};

std::string name = j["name"];          // string
int age = j["age"];                    // int
bool is_student = j["is_student"];     // bool
std::vector<int> scores = j["scores"]; // vector<int>
double score = j["scores"][0];         // double (隐式转换)
std::nullptr_t meta = j["metadata"];   // nullptr_t (或直接判断是否为 null)

C++ → JSON

json j;
j["name"] = std::string("Alice");      // string
j["age"] = 25;                         // int
j["pi"] = 3.14159;                     // double
j["is_valid"] = true;                  // bool
j["tags"] = {"cpp", "json", "nlohmann"}; // vector<string>
j["empty"] = nullptr;                  // null

2. 特殊类型处理

  • 嵌套对象/数组​:自动递归转换。 json nested = { {"user", {{"name", "Tom"}, {"age", 40}}}, {"ids", {1, 2, 3}} }; std::string user_name = nested["user"]["name"]; // "Tom"
  • 自定义类型​:需手动实现序列化(见进阶部分)。

四、进阶操作

1. 遍历 JSON 数据

遍历对象

json j = {{"name", "Alice"}, {"age", 25}, {"city", "Beijing"}};
for (auto& [key, value] : j.items()) { // C++17 结构化绑定
    std::cout << key << ": " << value << std::endl;
}
// 输出:
// name: "Alice"
// age: 25
// city: "Beijing"

遍历数组

json arr = {"a", "b", "c"};
for (const auto& item : arr) {
    std::cout << item << std::endl; // 依次输出 a, b, c
}

2. 合并与合并 JSON

json j1 = {{"name", "Alice"}, {"age", 25}};
json j2 = {{"city", "Beijing"}, {"hobbies", {"reading"}}};

// 合并 j2 到 j1(j1 被修改)
j1.update(j2);
// j1 现在为 {"name":"Alice","age":25,"city":"Beijing","hobbies":["reading"]}

3. 序列化为字符串/文件

json j = {{"name", "Bob"}, {"age", 30}};

// 转为字符串
std::string json_str = j.dump();          // 无格式(紧凑)
std::string pretty_str = j.dump(4);       // 缩进4空格(美化)

// 写入文件
std::ofstream out("output.json");
out << pretty_str; // 写入美化后的 JSON

五、错误处理

1. 解析错误

try {
    json j = json::parse("{invalid_json}"); // 非法格式
} catch (const json::parse_error& e) {
    std::cerr << "Parse error: " << e.what() 
              << " (offset: " << e.byte << ")" << std::endl;
}

2. 访问错误

json j = {{"name", "Alice"}};
try {
    int age = j.at("age"); // 键不存在
} catch (const json::out_of_range& e) {
    std::cerr << "Key not found: " << e.what() << std::endl;
}

六、完整示例

#include <iostream>
#include <nlohmann/json.hpp>
#include <fstream>
#include <vector>

using json = nlohmann::json;

int main() {
    // 1. 创建 JSON
    json person = {
        {"name", "Alice"},
        {"age", 25},
        {"hobbies", {"reading", "coding"}},
        {"address", {
            {"city", "Beijing"},
            {"zip", "100000"}
        }}
    };

    // 2. 序列化为字符串
    std::string json_str = person.dump(2); // 缩进2空格
    std::cout << "Serialized JSON:\n" << json_str << std::endl;

    // 3. 写入文件
    std::ofstream file("person.json");
    file << person.dump(4); // 美化输出
    file.close();

    // 4. 从文件读取并解析
    std::ifstream in("person.json");
    json loaded_person = json::parse(in);
    std::cout << "Loaded name: " << loaded_person["name"] << std::endl;

    // 5. 遍历嵌套对象
    for (auto& [key, value] : loaded_person["address"].items()) {
        std::cout << key << ": " << value << std::endl;
    }

    return 0;
}

七、注意事项

  1. 性能​:nlohmann/json 在解析和生成时性能较高,但对超大型 JSON 文件(GB 级别)建议使用流式解析(如 json::parse(sax) 接口)。
  2. 线程安全​:单个 json 对象非线程安全,多线程操作需加锁。
  3. 自定义类型​:若需序列化自定义类,需实现 to_json(json&, const T&)from_json(const json&, T&) 函数(详见官方文档)。

通过本指南,你可以快速掌握 nlohmann/json 的核心功能,高效处理 JSON 数据!

© 版权声明
THE END
喜欢就点个赞,支持一下吧!
点赞5 分享
评论 抢沙发
头像
欢迎您留下评论!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容