JSON是一种轻量级、跨语言的数据交换格式.

JSON 核心结构与数据类型

JSON 只有两种基础结构,可嵌套组合出复杂数据:

1. 两种基础结构
结构类型 语法表示 描述 示例
对象(Object) {} 包裹,键值对用 : 分隔,键值对间用 , 分隔 无序的键值对集合,键必须是字符串(双引号包裹) {"id": 1001, "name": "zhangsan", "online": true}
数组(Array) [] 包裹,元素间用 , 分隔 有序的元素集合,元素可以是任意 JSON 数据类型 ["apple", 20, {"color": "red"}]
(1)对象(Object):无序键值对集合
  • 语法:{} 包裹,键值对用 : 分隔,多个键值对用 , 分隔;
  • 强制规则:键必须是双引号包裹的字符串(单引号 / 无引号均非法);值可以是任意 JSON 数据类型;
  • 示例(聊天系统用户信息):
    {
      "id": 1001,
      "name": "zhangsan",
      "password": "123456",
      "online": true,
      "friends": [1002, 1003],
      "info": {"age": 20, "gender": "male"}
    }
    
(2)数组(Array):有序元素集合
  • 语法:[] 包裹,元素间用 , 分隔;
  • 规则:元素类型无限制(字符串 / 数字 / 布尔 /null/ 对象 / 数组),允许混合类型;
  • 示例(聊天系统离线消息):
    [
      {"from": 1002, "content": "你好", "time": "2025-12-07"},
      {"from": 1003, "content": "在吗?", "time": "2025-12-07"},
      ["系统通知", "账号已登录"]
    ]
2. 支持的数据类型

JSON 的值可以是以下 6 种类型(注意:没有 “注释” 语法,也没有 “undefined”“函数” 等类型):

类型 描述 示例
字符串 双引号包裹的 Unicode 字符(不能用单引号) "hello""中文测试"
数字 整数 / 浮点数(支持正负、科学计数法) 123-45.61e3
布尔值 仅 true 或 false(小写) true
null 表示空值(小写) null
对象(Object) 嵌套的键值对集合 {"age": 20}
数组(Array) 嵌套的元素集合 [1, 2, 3]

C++ 操作 JSON(nlohmann/json 全量实战)

C++ 标准库无 JSON 支持,nlohmann/json 是工业级首选(单头文件、无需编译、兼容 C++11+、功能最全),以下是其全量操作指南。

1. 环境准备

  • 方式 1(推荐):下载单头文件 json.hpp,放入项目目录,直接 #include "json.hpp"

  • 方式 2(包管理):Ubuntu sudo apt install nlohmann-json3-dev,CentOS yum install nlohmann-json-devel

  • 命名空间简化:using json = nlohmann::json;

2. 核心操作:构造 JSON

(1)直接初始化(静态构造)

适合已知数据结构的场景,简洁高效:

#include <iostream>
#include "json.hpp"
using json = nlohmann::json;
using namespace std;

int main() {
    // 构造对象
    json user = {
        {"id", 1001},
        {"name", "zhangsan"},
        {"online", true},
        {"friends", {1002, 1003}}, // 嵌套数组
        {"info", {{"age", 20}, {"gender", "male"}}} // 嵌套对象
    };

    // 构造数组
    json offline_msgs = {
        {{"from", 1002}, {"content", "你好"}, {"time", "2025-12-07"}},
        {{"from", 1003}, {"content", "在吗?"}, {"time", "2025-12-07"}}
    };

    // 打印(dump() 序列化)
    cout << "用户对象:" << user.dump(4) << endl; // 格式化输出(缩进4空格)
    cout << "离线消息数组:" << offline_msgs.dump() << endl; // 紧凑输出
    return 0;
}
(2)动态构造(运行时添加)

适合数据结构不确定的场景(如根据业务逻辑添加字段):

int main() {
    // 空对象 + 动态添加字段
    json resp;
    resp["code"] = 0; // 数字
    resp["msg"] = "success"; // 字符串
    resp["data"] = json::object(); // 嵌套空对象
    resp["data"]["userid"] = 1001;
    resp["data"]["friends"] = json::array(); // 嵌套空数组
    resp["data"]["friends"].push_back(1002);
    resp["data"]["friends"].push_back(1003);

    // 空数组 + 动态添加元素
    json arr;
    arr.emplace_back("hello"); // 原地构造,比push_back更高效
    arr.emplace_back(123);
    arr.emplace_back(json::object({{"key", "value"}}));

    cout << "动态构造的响应:" << resp.dump(4) << endl;
    return 0;
}
(3)从 C++ 容器转换(自动适配)

支持 std::map/std::unordered_map/std::vector/std::list 等容器直接转换为 JSON:

int main() {
    // vector → JSON数组
    vector<int> vec = {1001, 1002, 1003};
    json arr = vec;
    cout << "vector转数组:" << arr.dump() << endl; // [1001,1002,1003]

    // map → JSON对象
    map<string, string> mp = {{"name", "zhangsan"}, {"password", "123456"}};
    json obj = mp;
    cout << "map转对象:" << obj.dump() << endl; // {"name":"zhangsan","password":"123456"}

    return 0;
}

3. 核心操作:解析 JSON(字符串 → JSON 对象 / 数组)

将客户端 / 文件传来的 JSON 字符串解析为可操作的 JSON 对象,必须加异常捕获(避免非法格式导致崩溃)。

(1)基础解析
int main() {
    // 模拟网络接收的JSON字符串(聊天系统登录请求)
    string req_str = R"({"type":"login","id":"1001","password":"123456"})";

    try {
        // 解析字符串为JSON对象
        json req = json::parse(req_str);

        // 访问字段(推荐at(),越界抛异常;[] 越界会自动插入,不推荐)
        string type = req.at("type"); // "login"
        string id_str = req.at("id"); // "1001"
        int id = stoi(id_str); // 字符串转数字

        cout << "解析登录请求:type=" << type << ", id=" << id << endl;
    } catch (const json::parse_error& e) {
        // 解析格式错误(如尾逗号、单引号、语法混乱)
        cerr << "JSON解析失败:" << e.what() << ",原始字符串:" << req_str << endl;
    } catch (const json::out_of_range& e) {
        // 字段不存在(如访问req.at("non_exist_key"))
        cerr << "JSON字段不存在:" << e.what() << endl;
    } catch (const exception& e) {
        // 其他异常(如stoi转换失败)
        cerr << "处理失败:" << e.what() << endl;
    }

    return 0;
}
(2)高级解析:自定义参数
// 解析时忽略注释(JSON本身无注释,但可兼容//或/* */注释)
json req = json::parse(req_str, nullptr, true); // 第三个参数:ignore_comments=true

// 解析时允许尾逗号(兼容不规范的JSON)
json req = json::parse(req_str, nullptr, true, true); // 第四个参数:allow_trailing_commas=true

4. 核心操作:序列化(JSON 对象 → 字符串)

将 JSON 对象 / 数组转换为字符串,用于网络发送 / 文件存储,支持 “紧凑格式” 和 “格式化格式”。

int main() {
    json resp = {
        {"code", 0},
        {"msg", "login success"},
        {"data", {{"id", 1001}, {"name", "zhangsan"}}}
    };

    // 紧凑格式(默认,体积小,适合网络传输)
    string resp_compact = resp.dump();
    cout << "紧凑格式:" << resp_compact << endl;
    // 输出:{"code":0,"msg":"login success","data":{"id":1001,"name":"zhangsan"}}

    // 格式化格式(缩进4空格,易读,适合日志/调试)
    string resp_pretty = resp.dump(4);
    cout << "格式化格式:" << endl << resp_pretty << endl;
    /* 输出:
    {
        "code": 0,
        "msg": "login success",
        "data": {
            "id": 1001,
            "name": "zhangsan"
        }
    }
    */

    return 0;
}

5. 核心操作:遍历 JSON

(1)遍历对象(键值对)
int main() {
    json user = {{"id", 1001}, {"name", "zhangsan"}, {"online", true}};

    // 方式1:范围for(C++17+,推荐)
    for (auto& [key, value] : user.items()) {
        cout << "键:" << key << ",值:" << value << ",类型:" << value.type_name() << endl;
    }

    // 方式2:迭代器遍历(兼容C++11+)
    for (auto it = user.begin(); it != user.end(); ++it) {
        cout << "键:" << it.key() << ",值:" << it.value() << endl;
    }

    return 0;
}
(2)遍历数组(元素)
int main() {
    json arr = {1001, 1002, 1003, {"name", "zhangsan"}};

    // 方式1:范围for(推荐)
    for (auto& elem : arr) {
        cout << "元素:" << elem;
        if (elem.is_object()) { // 判断元素类型
            cout << "(对象类型)";
        }
        cout << endl;
    }

    // 方式2:下标遍历(知道长度)
    for (size_t i = 0; i < arr.size(); ++i) {
        cout << "索引" << i << ":" << arr.at(i) << endl;
    }

    return 0;
}

6. 核心操作:修改 / 删除 JSON

int main() {
    json user = {{"id", 1001}, {"name", "zhangsan"}, {"friends", {1002, 1003}}};

    // 1. 修改字段值
    user.at("name") = "张三"; // 字符串修改
    user.at("friends").at(0) = 1004; // 数组元素修改

    // 2. 添加新字段/元素
    user["age"] = 20; // 对象添加字段
    user["friends"].push_back(1005); // 数组追加元素

    // 3. 删除字段/元素
    user.erase("password"); // 删除对象字段(不存在则无操作)
    user["friends"].pop_back(); // 删除数组最后一个元素
    user["friends"].erase(user["friends"].begin() + 1); // 删除数组指定位置元素

    // 4. 清空对象/数组
    json empty_obj = json::object(); // 空对象 {}
    json empty_arr = json::array(); // 空数组 []
    user["friends"].clear(); // 清空数组

    cout << "修改后的JSON:" << user.dump(4) << endl;
    return 0;
}

7. 核心操作:类型判断与转换

避免类型不匹配导致的崩溃,操作前先判断类型:

int main() {
    json data = {
        {"id", "1001"}, // 字符串类型的ID
        {"age", 20},    // 数字类型的年龄
        {"friends", [1002, 1003]}, // 数组
        {"info", nullptr} // null
    };

    // 1. 类型判断(常用API)
    cout << "id是否为字符串:" << data["id"].is_string() << endl; // true
    cout << "age是否为数字:" << data["age"].is_number() << endl; // true
    cout << "friends是否为数组:" << data["friends"].is_array() << endl; // true
    cout << "info是否为null:" << data["info"].is_null() << endl; // true

    // 2. 安全转换为C++类型
    string id_str = data["id"].get<string>(); // 转字符串
    int age = data["age"].get<int>(); // 转int
    vector<int> friends = data["friends"].get<vector<int>>(); // 数组转vector

    // 3. 类型转换异常捕获
    try {
        int id = data["id"].get<int>(); // 字符串转int,抛异常
    } catch (const json::type_error& e) {
        cerr << "类型转换失败:" << e.what() << endl;
    }

    return 0;
}

实战场景:聊天系统中的 JSON 应用

场景 1:客户端 → 服务器:登录请求

// 客户端发送的JSON(字符串):
// {"type":"login","userid":"1001","password":"123456"}
void handleLogin(const string& recv_str) {
    try {
        json req = json::parse(recv_str);
        // 校验必选字段
        if (!req.contains("type") || !req.contains("userid") || !req.contains("password")) {
            throw runtime_error("缺少必选字段");
        }
        // 解析字段
        string type = req["type"];
        int userid = stoi(req["userid"].get<string>());
        string password = req["password"];

        // 业务逻辑:校验密码、更新登录状态
        bool login_ok = checkPassword(userid, password);
        // 构造响应
        json resp;
        if (login_ok) {
            resp["code"] = 0;
            resp["msg"] = "登录成功";
            resp["data"] = {{"userid", userid}, {"name", getUserName(userid)}};
        } else {
            resp["code"] = -1;
            resp["msg"] = "密码错误";
        }
        // 发送响应给客户端
        sendToClient(resp.dump());
    } catch (const exception& e) {
        json err_resp = {{"code", -2}, {"msg", "登录失败:" + string(e.what())}};
        sendToClient(err_resp.dump());
    }
}

场景 2:服务器 → 客户端:批量返回离线消息

// 业务逻辑:查询用户1001的离线消息,构造JSON数组响应
json getOfflineMsgResp(int userid) {
    json resp;
    resp["code"] = 0;
    resp["msg"] = "success";
    
    // 从数据库查询离线消息
    vector<OfflineMsg> msgs = queryOfflineMsg(userid);
    json msg_arr;
    for (auto& msg : msgs) {
        msg_arr.push_back({
            {"from", msg.from_id},
            {"content", msg.content},
            {"time", msg.create_time}
        });
    }
    resp["offline_msgs"] = msg_arr; // 空数组则返回[],而非null
    return resp;
}

// 发送响应:{"code":0,"msg":"success","offline_msgs":[{"from":1002,"content":"你好","time":"2025-12-07"},...]}
string resp_str = getOfflineMsgResp(1001).dump();
sendToClient(resp_str);

场景 3:客户端 → 服务器:批量添加好友(数组)

// 客户端发送的JSON:
// {"type":"batch_add_friend","userid":"1001","friend_ids":["1002","1003","1004"]}
void handleBatchAddFriend(const string& recv_str) {
    try {
        json req = json::parse(recv_str);
        int userid = stoi(req["userid"].get<string>());
        json friend_ids = req["friend_ids"];

        // 校验是否为数组
        if (!friend_ids.is_array()) {
            throw runtime_error("friend_ids必须是数组");
        }

        // 遍历数组,批量添加好友(事务保证原子性)
        for (auto& fid : friend_ids) {
            if (!fid.is_string()) {
                cerr << "好友ID格式错误:" << fid << endl;
                continue;
            }
            int friendid = stoi(fid.get<string>());
            addFriendWithTransaction(userid, friendid); // 事务操作
        }

        // 构造响应
        json resp = {{"code", 0}, {"msg", "批量添加好友成功"}};
        sendToClient(resp.dump());
    } catch (const exception& e) {
        json err_resp = {{"code", -1}, {"msg", "批量添加失败:" + string(e.what())}};
        sendToClient(err_resp.dump());
    }
}

进阶:性能优化与特殊场景

1. 性能优化(高并发场景)

  • 避免频繁解析 / 序列化:将解析后的 JSON 对象缓存,而非每次都解析字符串;
  • 使用 stream 解析 / 序列化:超大 JSON(如 100MB+)用 stream 避免内存暴涨:
    // stream解析(从文件/网络流读取)
    ifstream ifs("large.json");
    json data;
    ifs >> data;
    
    // stream序列化(写入文件/网络流)
    ofstream ofs("output.json");
    ofs << data.dump(4);
    
  • 减少拷贝:用 std::move 转移 JSON 对象所有权:
    json createResp() {
        json resp = {{"code", 0}};
        return std::move(resp); // 避免拷贝
    }
    

2. 特殊场景处理

(1)超大 JSON(GB 级)
  • 用 nlohmann::json::parser 流式解析,逐行 / 逐字段处理,不加载整个 JSON 到内存;
  • 避免 dump() 一次性序列化,分块输出。
(2)二进制 JSON(MessagePack)

nlohmann/json 支持 MessagePack(二进制 JSON,体积更小、解析更快):

// JSON → MessagePack
json data = {{"id", 1001}, {"name", "zhangsan"}};
vector<uint8_t> msgpack_data = json::to_msgpack(data);

// MessagePack → JSON
json data2 = json::from_msgpack(msgpack_data);
(3)兼容不规范 JSON
  • 允许尾逗号:json::parse(str, nullptr, true, true)
  • 忽略注释:json::parse(str, nullptr, true)
  • 单引号转双引号:解析前手动替换 str.replace(str.find("'"), 1, "\"")

常见错误与解决方案(排错速查)

错误类型 错误原因 解决方案
json::parse_error 1. 尾逗号;2. 单引号;3. 语法混乱;4. 空字符串 1. 移除尾逗号;2. 单引号转双引号;3. 校验 JSON 语法;4. 空字符串返回错误
json::out_of_range 访问不存在的键 / 数组索引越界 1. 用 at () 访问;2. 访问前用 contains () 判断键是否存在;3. 数组索引 < size ()
json::type_error 类型不匹配(如字符串转 int、数组当对象用) 1. 操作前用 is_*() 判断类型;2. 显式转换(如先转字符串再 stoi)
中文乱码 JSON 字符串编码非 UTF-8 1. 客户端 / 服务器统一 UTF-8 编码;2. 序列化 / 解析时指定 UTF-8
数字精度丢失 超大数字(如 64 位整数)用数字类型存储 1. 超大数字用字符串存储;2. 用 get<uint64_t>() 转换为 64 位整数

JSON vs 其他格式(选型参考)

格式 优点 缺点 适用场景
JSON 易读、跨语言、轻量 体积比二进制大、无注释 网络通信、配置文件、轻量数据
XML 规范完善、支持注释 体积大、解析慢 传统系统、SOAP 接口
Protobuf 体积极小、解析极快 不可读、需定义协议 高并发 / 高性能网络通信
MessagePack 二进制 JSON、体积小 不可读 兼容 JSON 的高性能场景

总结

JSON 的核心是 “2 种结构(对象 / 数组)+6 种类型”,语法规则严格但简单,C++ 中用 nlohmann/json 可实现全量操作。在网络通信(如聊天系统)中,需注意:

  1. 解析 / 序列化必须加异常捕获,避免非法格式导致崩溃;
  2. 字段访问优先用 at (),避免越界;
  3. 类型操作前先判断,避免类型不匹配;
  4. 高并发场景优化解析 / 序列化性能,超大 JSON 用流式处理。

掌握以上内容,可覆盖 99% 的 JSON 使用场景,包括日常开发、高并发优化、排错等全维度需求。

详细介绍一下JSON的语法规则

分享一些在C++中使用nlohmann/json库的代码示例

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐