C++ Jsoncpp 完整使用教程:序列化反序列化+TCP网络项目实战

前言

在C++网络开发中,JSON是最常用的数据交换格式,Jsoncpp作为成熟开源库,能够快速实现内存对象与JSON字符串互转。本文结合TCP自定义通信协议场景,从基础概念、核心类API、序列化/反序列化实操、完整项目落地全方位讲解,覆盖Json::ValueJson::ReaderFastWriterStreamWriter等全部核心组件,适配后端网络业务开发。

一、基础概念铺垫

1.1 序列化与反序列化核心定义

  • 序列化:将内存中C++结构体/自定义业务类,转为JSON字符串字节流,用于本地文件存储、TCP网络传输。
  • 反序列化:接收网络字符串/读取文件后,将JSON文本还原为C++内存对象,供业务逻辑读取计算。

1.2 项目业务流转场景(TCP通信)

  1. 发送流程:业务Request对象 → Json序列化JSON字符串 → 协议封装长度\r\n内容\r\n→ Socket发送
  2. 接收流程:Socket读取字节流 → 解码拆分完整JSON报文 → Json反序列化还原Request对象 → 执行业务计算

1.3 Jsoncpp核心组件总览

Jsoncpp所有能力分为三大模块:数据容器、序列化输出工具、反序列化解析工具。

类名核心作用使用场景
Json::ValueJSON通用数据容器,支持对象、数组、数字、字符串、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数组操作

  1. append():数组尾部追加元素
  2. [下标]:下标访问数组元素,越界自动扩容
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 分层业务架构

  1. JSON序列化层:业务对象 ↔ JSON字符串
  2. 协议编解码层:JSON字符串 ↔ 带长度前缀报文(解决TCP粘包)

5.2 发送端完整流程

  1. 实例化Request业务对象,填充运算数据
  2. 调用Serialize(),FastWriter序列化JSON字符串
  3. Encode()封装长度\r\n内容\r\n协议头
  4. Socket发送完整报文

5.3 接收端完整流程

  1. recv读取字节流存入缓冲区
  2. Decode()根据长度拆分完整JSON载荷
  3. Deserialize()解析JSON还原业务对象
  4. 执行加减乘除业务计算

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 -ljsoncpp

6.2 高频注意事项

  1. 键名大小写敏感dataxDataX是两个独立字段,序列化、反序列化key必须完全一致;
  2. 类型安全校验:取值前先用isXXX()判断类型,不同类型直接转换会导致程序崩溃;
  3. JSON无法解决TCP粘包:JSON仅负责数据结构化,字节流粘包必须依靠「长度前缀」协议编码处理;
  4. 网络传输优先FastWriter:格式化输出体积更大,增加网络IO开销,仅本地调试使用;
  5. 新版项目推荐StreamWriter:FastWriter/StyledWriter属于旧版API,官方逐步迭代废弃。

总结

  1. Json::Value是Jsoncpp唯一数据载体,所有JSON对象、数组、基础类型都通过该类存储;
  2. 序列化分三类场景:网络传输用FastWriter、调试用StyledWriter、新项目统一使用StreamWriter;
  3. Json::Reader负责解析JSON文本,务必增加解析失败判断与错误日志打印;
  4. 网络开发中JSON仅做数据转换,TCP粘包问题需要自定义长度协议配合解决;
  5. 实际项目建议封装序列化/反序列化工具函数,统一管理编解码逻辑,减少重复代码。