C++ Jsoncpp 完整使用教程:序列化反序列化+TCP网络项目实战
前言
在C++网络开发中,JSON是最常用的数据交换格式,Jsoncpp作为成熟开源库,能够快速实现内存对象与JSON字符串互转。本文结合TCP自定义通信协议场景,从基础概念、核心类API、序列化/反序列化实操、完整项目落地全方位讲解,覆盖Json::Value、Json::Reader、FastWriter、StreamWriter等全部核心组件,适配后端网络业务开发。
一、基础概念铺垫
1.1 序列化与反序列化核心定义
- 序列化:将内存中C++结构体/自定义业务类,转为JSON字符串字节流,用于本地文件存储、TCP网络传输。
- 反序列化:接收网络字符串/读取文件后,将JSON文本还原为C++内存对象,供业务逻辑读取计算。
1.2 项目业务流转场景(TCP通信)
- 发送流程:业务Request对象 → Json序列化JSON字符串 → 协议封装
长度\r\n内容\r\n→ Socket发送 - 接收流程:Socket读取字节流 → 解码拆分完整JSON报文 → Json反序列化还原Request对象 → 执行业务计算
1.3 Jsoncpp核心组件总览
Jsoncpp所有能力分为三大模块:数据容器、序列化输出工具、反序列化解析工具。
| 类名 | 核心作用 | 使用场景 |
|---|---|---|
| Json::Value | JSON通用数据容器,支持对象、数组、数字、字符串、bool、null全部JSON类型 | 序列化/反序列化中间载体,所有数据读写都依赖该类 |
| Json::FastWriter | 序列化工具,输出无换行无缩进紧凑单行JSON | 网络传输、接口上报(体积最小,推荐项目使用) |
| Json::StyledWriter | 序列化工具,格式化带缩进换行 | 日志打印、本地调试查看JSON结构 |
| Json::StreamWriter | 新版官方序列化标准工具,支持自定义缩进、分隔符 | 新项目、需要灵活定制输出格式场景 |
| Json::Reader | 反序列化解析工具,JSON字符串转Json::Value,自带错误日志 | 接收网络报文、读取JSON文件解析 |
二、核心容器:Json::Value 全API详解
Json::Value是整个库的核心,序列化前必须把数据存入该对象;解析JSON后的结果也统一存储在此类中。
2.1 常用构造函数
| 构造写法 | 说明 |
|---|---|
| Json::Value val; | 默认构造,初始为null空值 |
| Json::Value val(Json::objectValue); | 指定类型创建空JSON对象(可选arrayValue/intValue/stringValue/nullValue) |
| Json::Value val(100); | 直接传入数字,自动识别int类型 |
| Json::Value val("test"); | 直接传入字符串,自动识别string类型 |
2.2 JSON对象(键值对)读写操作
重载[]运算符(最常用)
通过字符串key访问对象字段;key不存在时会自动创建key,值默认null。
Json::Value root; root["username"] = "zhangsan"; root["age"] = 24; root["isVip"] = true;at()方法(严格校验)
功能与[]一致,key不存在直接抛出异常,适合需要强校验、防止非法字段场景。
std::string name = root.at("username").asString();2.3 JSON数组操作
append():数组尾部追加元素[下标]:下标访问数组元素,越界自动扩容
Json::Value arr; arr.append(11); arr.append(22); arr.append("json测试"); int num = arr[0].asInt(); // 获取第一个元素2.4 类型判断接口(规避类型转换崩溃)
取值前优先调用,校验存储数据真实类型:
| 方法 | 功能说明 |
|---|---|
| isNull() | 是否为空null |
| isBool() | 是否布尔值 |
| isInt()/isInt64() | 32/64位有符号整数 |
| isUInt()/isUInt64() | 32/64位无符号整数 |
| isDouble() | 浮点小数 |
| isNumeric() | 任意数字(int/double) |
| isString() | 字符串类型 |
| isArray() | JSON数组 |
| isObject() | JSON键值对象 |
2.5 类型转换取值方法
反序列化后从Json::Value取出数据转为原生C++类型:
| 方法 | 转换类型 |
|---|---|
| asBool() | bool |
| asInt()/asInt64() | 有符号整数 |
| asUInt()/asUInt64() | 无符号整数 |
| asDouble() | double浮点数 |
| asString() | std::string字符串 |
2.6 通用工具方法
| 方法 | 功能 |
|---|---|
| size() | 对象返回键总数,数组返回元素个数 |
| empty() | 判断容器是否无数据 |
| clear() | 清空所有键/数组元素 |
| resize(newSize) | 仅数组可用,调整数组长度 |
三、序列化:Json::Value → JSON字符串
提供4种序列化方案,根据网络传输、调试、新项目标准场景区分使用。
3.1 Json::FastWriter(项目首选,网络传输)
输出单行紧凑JSON,无多余空格换行,报文体积最小,TCP通信推荐。
#include <iostream> #include <string> #include <jsoncpp/json/json.h> int main() { Json::Value root; root["name"] = "joe"; root["sex"] = "男"; root["age"] = 25; Json::FastWriter writer; std::string json_str = writer.write(root); // 输出:{"age":25,"name":"joe","sex":"男"} std::cout << json_str << std::endl; return 0; }核心API:std::string write(const Json::Value& root),输入Value对象,返回JSON字符串。
3.2 Json::StyledWriter(调试打印专用)
带缩进、换行格式化输出,可读性强,仅用于日志调试,不适合网络传输(报文偏大)。
Json::Value root; root["name"] = "joe"; root["sex"] = "男"; Json::StyledWriter writer; std::string json_str = writer.write(root); std::cout << json_str << std::endl;输出效果:
{ "name" : "joe", "sex" : "男" }3.3 toStyledString() 快捷格式化
无需创建Writer实例,直接调用Value成员方法,等价StyledWriter效果:
std::string json_str = root.toStyledString();3.4 Json::StreamWriter(新版官方标准写法)
官方推荐替代FastWriter/StyledWriter,支持自定义缩进、分隔符,灵活可控。
#include <iostream> #include <string> #include <sstream> #include <memory> #include <jsoncpp/json/json.h> int main() { Json::Value root; root["name"] = "joe"; root["sex"] = "男"; // 构造工厂 Json::StreamWriterBuilder wbuilder; // 置空缩进,实现和FastWriter一致的紧凑输出 wbuilder["indentation"] = ""; std::unique_ptr<Json::StreamWriter> writer(wbuilder.newStreamWriter()); std::stringstream ss; writer->write(root, &ss); std::cout << ss.str() << std::endl; return 0; }四、反序列化:JSON字符串 → Json::Value
核心解析类Json::Reader,接收JSON文本,解析填充至Json::Value,并返回解析状态与错误信息。
4.1 Reader核心API说明
parse(const std::string& document, Json::Value& root):解析字符串到Value- 返回值
bool:true解析成功,false解析失败 getFormattedErrorMessages():获取格式化错误日志,定位JSON语法错误
4.2 基础解析示例
#include <iostream> #include <string> #include <jsoncpp/json/json.h> int main() { // 模拟网络接收的JSON报文 std::string json_string = "{\"name\":\"张三\", \"age\":30, \"city\":\"北京\"}"; Json::Reader reader; Json::Value root; bool parse_ok = reader.parse(json_string, root); if (!parse_ok) { // 打印解析失败详情 std::cout << "JSON解析失败:" << reader.getFormattedErrorMessages() << std::endl; return -1; } // 提取字段 std::string name = root["name"].asString(); int age = root["age"].asInt(); std::cout << "姓名:" << name << " 年龄:" << age << std::endl; return 0; }4.3 业务类反序列化封装示例
项目中封装成统一接口,直接将JSON转为业务对象:
// 业务类反序列化方法 bool Deserialize(std::string &json_buf) { Json::Value root; Json::Reader reader; bool res = reader.parse(json_buf, root); if(res) { // 从JSON读取数据赋值成员变量 _data_x = root["datax"].asInt(); _data_y = root["datay"].asInt(); _oper = static_cast<char>(root["oper"].asInt()); } return res; }五、TCP网络项目完整实战(自定义协议)
5.1 分层业务架构
- JSON序列化层:业务对象 ↔ JSON字符串
- 协议编解码层:JSON字符串 ↔ 带长度前缀报文(解决TCP粘包)
5.2 发送端完整流程
- 实例化Request业务对象,填充运算数据
- 调用
Serialize(),FastWriter序列化JSON字符串 Encode()封装长度\r\n内容\r\n协议头- Socket发送完整报文
5.3 接收端完整流程
- recv读取字节流存入缓冲区
Decode()根据长度拆分完整JSON载荷Deserialize()解析JSON还原业务对象- 执行加减乘除业务计算
5.4 可运行完整Demo
#include "Protocol.hpp" #include <iostream> int main() { // 发送端逻辑 Protocol::Request req(10, 20, '+'); std::string json_str; req.Serialize(&json_str); std::cout << "序列化JSON:" << json_str << std::endl; // 协议编码,增加长度前缀防粘包 std::string send_package = Protocol::Encode(json_str); std::cout << "编码后完整报文:" << send_package << std::endl; // 模拟网络传输 std::string recv_buffer = send_package; // 接收端逻辑 std::string recv_json; bool decode_ok = Protocol::Decode(recv_buffer, &recv_json); if(!decode_ok) { std::cout << "报文解码失败,数据不完整" << std::endl; return -1; } // 反序列化还原对象 Protocol::Request recv_req; recv_req.Deserialize(recv_json); std::cout << "解析结果:" << recv_req.GetX() << recv_req.GetOper() << recv_req.GetY() << std::endl; return 0; }六、编译配置与开发避坑指南
6.1 头文件引入&编译命令
引入头文件
#include <jsoncpp/json/json.h>g++编译链接库
g++ main.cpp -o json_demo -ljsoncpp6.2 高频注意事项
- 键名大小写敏感:
datax和DataX是两个独立字段,序列化、反序列化key必须完全一致; - 类型安全校验:取值前先用
isXXX()判断类型,不同类型直接转换会导致程序崩溃; - JSON无法解决TCP粘包:JSON仅负责数据结构化,字节流粘包必须依靠「长度前缀」协议编码处理;
- 网络传输优先FastWriter:格式化输出体积更大,增加网络IO开销,仅本地调试使用;
- 新版项目推荐StreamWriter:FastWriter/StyledWriter属于旧版API,官方逐步迭代废弃。
总结
Json::Value是Jsoncpp唯一数据载体,所有JSON对象、数组、基础类型都通过该类存储;- 序列化分三类场景:网络传输用FastWriter、调试用StyledWriter、新项目统一使用StreamWriter;
Json::Reader负责解析JSON文本,务必增加解析失败判断与错误日志打印;- 网络开发中JSON仅做数据转换,TCP粘包问题需要自定义长度协议配合解决;
- 实际项目建议封装序列化/反序列化工具函数,统一管理编解码逻辑,减少重复代码。
