跳到主要内容C++ JsonCpp 库基础与核心 API 使用指南 | 极客日志C++
C++ JsonCpp 库基础与核心 API 使用指南
综述由AI生成C++ JsonCpp 库是处理 JSON 数据的核心工具,支持解析、生成及修改操作。 JSON 基础规范、JsonCpp 安装配置、核心 API 如 Json::Value 类型判断与转换、序列化与反序列化方法。通过代码示例展示了对象、数组及嵌套结构的操作流程,并总结了常见坑点如 operator[] 副作用、线程安全及编码问题,最后对比了其他 C++ JSON 库特性,为开发者提供完整的使用参考。
晚风叙旧27 浏览 一、JSON 基础
在学习 JsonCpp 之前,我们先搞懂 JSON 的核心规范,这是后续的基础。
1.1 JSON 核心结构
JSON 仅包含两种核心结构,所有复杂数据均由二者嵌套组合而成:
- 对象:无序的键值对集合,使用大括号
{} 包裹,是 JSON 的核心复合类型
- 数组:有序的值集合,使用方括号
[] 包裹,用于存储同类型 / 多类型的有序数据
- 值:JSON 的最小单元,支持 6 种合法类型:字符串、数字、布尔值(true/false)、null、对象、数组
1.2 强制语法规则
JSON 的语法规范很严格,不符合规则的字符串会直接解析失败,规则如下:
- 所有键名必须是双引号包裹的 UTF-8 字符串,禁止单引号、无引号
- 键与值之间必须用英文冒号
: 分隔,多个键值对 / 数组元素用英文逗号 , 分隔
- 禁止末尾逗号(trailing comma),即最后一个键值对 / 数组元素后不能加逗号
- 字符串必须使用双引号包裹,支持转义字符(如
\n、\t、\"
- 数字支持整数、浮点数、科学计数法,无单双引号包裹
- 布尔值仅支持小写的
true/false,空值仅支持小写的 null
1.3 标准 JSON 示例
这里给出一份符合规范的完整 JSON 示例,覆盖所有核心类型:
{
"name": "John Doe",
"age": 30,
"is_student": false,
"weight": 75.5,
"skills": [ "JavaScript", "Python", "C++" ],
"address"
:
{
"street"
:
"123 Main St"
,
"city"
:
"Anytown"
,
"state"
:
"CA"
}
,
"education"
:
null
}
1.4 JSON 核心应用场景
- 数据交换:HTTP / 微服务 API 的标准数据传输格式,前后端、服务间通信的核心载体
- 配置文件:应用程序的配置管理,替代 ini、xml 等老旧格式,可读性与扩展性更强
- 数据存储:轻量级数据持久化,用于日志、用户配置、小型数据集的文件存储
- 数据序列化:跨语言、跨平台的数据序列化方案,兼容几乎所有主流编程语言
二、JsonCpp 库
JsonCpp 是一个专为 C++ 设计的高性能 JSON 处理库,完全兼容 JSON 标准规范,提供了 JSON 解析、生成、修改、遍历的全能力支持,是 C++ 生态中最成熟的 JSON 库之一。
2.1 核心特性优势
- 简单易用:提供直观的类 C++ 容器 API,学习成本极低,新手可快速上手
- 高性能:优化的解析与生成逻辑,可高效处理大批量 JSON 数据
- 全平台兼容:原生支持 Windows、Linux、macOS、嵌入式等几乎所有操作系统
- 无额外依赖:不依赖任何第三方库,可无缝集成到任何 C++ 项目中
- 标准兼容:完全支持 JSON 官方标准,同时提供注释、宽松解析等扩展能力
- 现代 C++ 支持:完美适配 C++11 及以上标准,支持智能指针、移动语义等现代特性
2.2 常见平台安装方法
2.2.1 Linux 系统(Debian/Ubuntu 系列)
sudo apt update && sudo apt install libjsoncpp-dev
sudo yum install jsoncpp-devel
2.2.2 macOS 系统
2.2.3 Windows 系统
推荐使用 vcpkg 包管理器安装,自动适配 Visual Studio 环境:
vcpkg install jsoncpp:x64-windows
2.2.4 源码编译安装(全平台通用)
2.3 开发环境基础配置
2.3.1 头文件引入
JsonCpp 的核心头文件有两种常见路径,根据安装方式适配:
- Debian/Ubuntu apt 安装:
#include <jsoncpp/json/json.h>
- 源码编译、brew、vcpkg 安装:
#include <json/json.h>
2.3.2 编译链接
编译时必须链接 jsoncpp 动态库,GCC/Clang 编译参数需添加:
Windows Visual Studio 需在项目属性中添加对应架构的 jsoncpp.lib 作为附加依赖项。
三、JsonCpp 核心 API
JsonCpp 的 API 设计极其清晰,核心分为三大模块:
Json::Value 核心数据类、StreamWriter 序列化模块、CharReader 反序列化模块
3.1 数据类型枚举 ValueType
JsonCpp 用枚举定义了 JSON 支持的所有数据类型,用于类型判断与校验,定义如下:
namespace Json {
enum ValueType {
nullValue = 0,
intValue,
uintValue,
realValue,
stringValue,
booleanValue,
arrayValue,
objectValue
};
}
3.2 核心数据类 Json::Value
Json::Value 是 JsonCpp 的核心,是一个变体类型,可存储任意 JSON 支持的数据类型,模拟了 JSON 的动态类型特性,提供了完整的类型判断、转换、读写能力。
3.2.1 类型判断接口
用于判断 Json::Value 存储的实际数据类型,是类型转换前的必操作,避免类型不匹配抛出异常:
| 接口方法 | 功能说明 |
|---|
bool isNull() const | 判断是否为 null 空值 |
bool isBool() const | 判断是否为布尔值 |
bool isInt() const | 判断是否为 32 位有符号整型 |
bool isInt64() const | 判断是否为 64 位有符号整型 |
bool isUInt() const | 判断是否为 32 位无符号整型 |
bool isUInt64() const | 判断是否为 64 位无符号整型 |
bool isDouble() const | 判断是否为浮点型 |
bool isNumeric() const | 判断是否为任意数字类型 (整型 / 浮点型) |
bool isString() const | 判断是否为字符串类型 |
bool isArray() const | 判断是否为数组类型 |
bool isObject() const | 判断是否为对象类型 |
3.2.2 类型转换接口
将 Json::Value 转换为对应的 C++ 原生类型,仅当类型匹配时可安全调用,否则会抛出 Json::Exception 异常:
| 接口方法 | 功能说明 |
|---|
const char* asCString() const | 转换为 C 风格 const char * 字符串,生命周期与原 Value 绑定 |
std::string asString() const | 转换为 std::string 字符串 |
Int asInt() const | 转换为 32 位有符号整型 int |
Int64 asInt64() const | 转换为 64 位有符号整型 long long |
UInt asUInt() const | 转换为 32 位无符号整型 unsigned int |
UInt64 asUInt64() const | 转换为 64 位无符号整型 unsigned long long |
float asFloat() const | 转换为单精度浮点型 float |
double asDouble() const | 转换为双精度浮点型 double |
bool asBool() const | 转换为布尔值 bool |
3.2.3 对象 (键值对) 操作接口
用于操作 JSON 对象的键值对,实现键的增删改查:
| 接口方法 | 功能说明 |
|---|
Value& operator[](const char* key) | 通过键名访问对应值,键不存在时自动插入 null 值 |
Value& operator[](const std::string& key) | 同上,支持 std::string 类型键名 |
bool isMember(const std::string& key) const | 判断对象中是否存在指定键名,无副作用 |
void removeMember(const std::string& key) | 删除对象中指定的键值对 |
std::vector<std::string> getMemberNames() const | 获取对象中所有键名的列表 |
3.2.4 数组操作接口
用于操作 JSON 数组,实现元素的增删改查与遍历:
| 接口方法 | 功能说明 |
|---|
ArrayIndex size() const | 获取数组元素个数,ArrayIndex 为 unsigned int 别名 |
bool empty() const | 判断数组是否为空 |
void clear() | 清空数组所有元素 |
void resize(ArrayIndex newSize) | 重置数组大小,扩容部分填充 null 值 |
Value& operator[](ArrayIndex index) | 通过下标访问数组元素,下标越界时自动扩容 |
Value& append(const Value& value) | 向数组末尾追加元素 |
3.2.5 辅助接口
| 接口方法 | 功能说明 |
|---|
void swap(Value& other) | 与另一个 Value 对象交换数据,无内存拷贝,高性能 |
void copy(const Value& other) | 深拷贝另一个 Value 对象的所有数据 |
std::string toStyledString() const | 快速序列化为带格式化的 JSON 字符串,调试专用 |
3.3 序列化核心:StreamWriter 系列 API
序列化指的是将 C++ 中的 Json::Value 对象转换为 JSON 格式字符串
核心类为 Json::StreamWriterBuilder(工厂类)与 Json::StreamWriter(序列化执行类)。
namespace Json {
class StreamWriter {
public:
virtual int write(Value const& root, std::ostream* sout) = 0;
};
class StreamWriterBuilder : public StreamWriter::Factory {
public:
Json::Value settings_;
StreamWriter* newStreamWriter() const override;
};
std::string writeString(StreamWriter::Factory const& factory, Value const& root);
}
| 配置项 | 取值 | 功能说明 |
|---|
commentStyle | "None"/"All" | 注释输出控制,None 为不输出注释,All 为输出所有注释 |
indentation | 字符串 | 缩进字符,默认 " "(两个空格),设为 "" 则生成无缩进的压缩 JSON |
emitUTF8 | true/false | 是否强制输出 UTF-8 编码,默认 true,建议保持开启 |
precision | 数字 | 浮点数序列化的精度,默认 15 位有效数字 |
3.4 反序列化核心:CharReader 系列 API
反序列化指的是将 JSON 格式字符串解析为 C++ 中的 Json::Value 对象
核心类为 Json::CharReaderBuilder(工厂类)与 Json::CharReader(反序列化执行类)。
namespace Json {
class CharReader {
public:
virtual bool parse(
char const* beginDoc, char const* endDoc,
Value* root, std::string* errs
) = 0;
};
class CharReaderBuilder : public CharReader::Factory {
public:
Json::Value settings_;
CharReader* newCharReader() const override;
};
}
| 配置项 | 取值 | 功能说明 |
|---|
allowComments | true/false | 是否支持 JSON 中的 // 单行注释与 /* */ 块注释,默认 false |
strictRoot | true/false | 是否严格要求根节点必须是对象或数组,默认 false |
allowTrailingCommas | true/false | 是否允许末尾逗号,默认 false |
failIfExtra | true/false | 解析到多余非空白字符时是否失败,默认 true |
四、基本使用方法和示例
(一)基础类型操作
1. 赋值与取值
#include <iostream>
#include <jsoncpp/json/json.h>
int main() {
Json::Value str_val;
str_val = "Hello JsonCpp";
if (str_val.isString())
{
std::cout << "字符串值:" << str_val.asString() << std::endl;
}
Json::Value int_val;
int_val = 100;
if (int_val.isInt())
{
std::cout << "整数值:" << int_val.asInt() << std::endl;
}
Json::Value double_val;
double_val = 3.14159;
if (double_val.isDouble())
{
std::cout << "浮点值:" << double_val.asDouble() << std::endl;
}
Json::Value bool_val;
bool_val = true;
if (bool_val.isBool())
{
std::cout << "布尔值:" << std::boolalpha << bool_val.asBool() << std::endl;
}
Json::Value null_val;
null_val = Json::nullValue;
if (null_val.isNull())
{
std::cout << "null 值:null" << std::endl;
}
return 0;
}
字符串值:Hello JsonCpp
整数值:100
浮点值:3.14159
布尔值:true
null 值:null
2. 关键方法说明
| 方法 | 作用 |
|---|
isString()/asString() | 判断 / 转换为字符串类型 |
isInt()/asInt() | 判断 / 转换为 32 位整数(也支持 isInt64/asInt64) |
isDouble()/asDouble() | 判断 / 转换为浮点类型 |
isBool()/asBool() | 判断 / 转换为布尔类型 |
isNull() | 判断是否为 null 值(无 asNull () 方法) |
(二)JSON 对象(键值对)操作
JSON 对象是无序键值对集合,核心操作是「增 / 删 / 改 / 查」键值对。
1. 完整示例
#include <iostream>
#include <vector>
#include <jsoncpp/json/json.h>
int main() {
Json::Value obj;
obj["name"] = "张三";
obj["age"] = 25;
obj["is_student"] = false;
obj["score"] = 95.5;
if (obj.isMember("name"))
{
std::cout << "姓名:" << obj["name"].asString() << std::endl;
}
std::vector<std::string> keys = obj.getMemberNames();
std::cout << "\n对象所有键值对:" << std::endl;
for (const auto& key : keys)
{
std::cout << key << " = " << obj[key] << std::endl;
}
obj.removeMember("score");
std::cout << "\n删除 score 后,是否还存在:" << std::boolalpha << obj.isMember("score") << std::endl;
std::string city = obj.get("city", "北京").asString();
std::cout << "城市(默认值):" << city << std::endl;
return 0;
}
姓名:张三
对象所有键值对:
name = "张三"
age = 25
is_student = false
score = 95.5
删除 score 后,是否还存在:false
城市(默认值):北京
2. 核心方法说明
| 方法 | 作用 |
|---|
operator[](const string& key) | 通过键访问值(无键则自动插入 null) |
isMember(const string& key) | 判断键是否存在(无副作用,必用) |
getMemberNames() | 获取所有键名的 vector 列表 |
removeMember(const string& key) | 删除指定键值对 |
get(key, defaultValue) | 安全取值(无键则返回默认值,无副作用) |
(三)JSON 数组操作
JSON 数组是有序值集合,核心操作是「追加 / 遍历 / 修改 / 清空」。
1. 完整示例
#include <iostream>
#include <jsoncpp/json/json.h>
int main() {
Json::Value arr;
arr.append("C++");
arr.append("Python");
arr.append(100);
arr.append(true);
std::cout << "数组长度:" << arr.size() << std::endl;
std::cout << "\n数组元素:" << std::endl;
for (int i = 0; i < arr.size(); ++i)
{
std::cout << "下标" << i << ":" << arr[i] << std::endl;
}
arr[1] = "Java";
std::cout << "\n修改后下标 1:" << arr[1].asString() << std::endl;
arr.clear();
std::cout << "清空后数组长度:" << arr.size() << std::endl;
arr.resize(3);
std::cout << "扩容后下标 2:" << (arr[2].isNull() ? "null" : "非 null") << std::endl;
return 0;
}
数组长度:4
数组元素:
下标 0:"C++"
下标 1:"Python"
下标 2:100
下标 3:true
修改后下标 1:Java
清空后数组长度:0
扩容后下标 2:null
2. 核心方法说明
| 方法 | 作用 |
|---|
append(const Value&) | 向数组末尾追加元素 |
size() | 获取数组元素个数(返回 ArrayIndex 类型,可转 int) |
operator[](int index) | 通过下标访问元素(越界则自动扩容) |
clear() | 清空数组所有元素 |
resize(int newSize) | 重置数组大小(扩容填充 null,缩容截断) |
empty() | 判断数组是否为空(size ()==0) |
(四)嵌套结构(对象嵌套对象 / 数组)
实际开发中最常用的场景,核心是「逐层创建 + 逐层访问」。
1. 完整示例
#include <iostream>
#include <jsoncpp/json/json.h>
int main() {
Json::Value addr;
addr["province"] = "广东省";
addr["city"] = "深圳市";
addr["street"] = "科技园路";
Json::Value hobbies;
hobbies.append("编程");
hobbies.append("阅读");
hobbies.append("跑步");
Json::Value root;
root["user"] = "李四";
root["age"] = 30;
root["address"] = addr;
root["hobbies"] = hobbies;
if (root.isMember("address") && root["address"].isObject())
{
std::cout << "省份:" << root["address"]["province"].asString() << std::endl;
}
if (root.isMember("hobbies") && root["hobbies"].isArray())
{
std::cout << "\n爱好列表:" << std::endl;
for (int i = 0; i < root["hobbies"].size(); ++i)
{
std::cout << " - " << root["hobbies"][i].asString() << std::endl;
}
}
std::cout << "\n完整 JSON 结构:" << std::endl;
std::cout << root.toStyledString() << std::endl;
return 0;
}
省份:广东省
爱好列表:
- 编程
- 阅读
- 跑步
完整 JSON 结构:
{
"address" : {
"city" : "深圳市",
"province" : "广东省",
"street" : "科技园路"
},
"age" : 30,
"hobbies" : [
"编程",
"阅读",
"跑步"
],
"user" : "李四"
}
(五)序列化(Value → JSON 字符串)
步骤构建 Json::Value 数据结构;配置 Json::StreamWriterBuilder(可选,默认紧凑输出);生成 JSON 字符串。
#include <json/json.h>
#include <iostream>
#include <sstream>
#include <string>
int main() {
Json::Value root;
root["name"] = "张三";
root["age"] = 25;
root["is_student"] = false;
Json::Value hobbies;
hobbies.append("篮球");
hobbies.append("编程");
root["hobbies"] = hobbies;
Json::Value address;
address["city"] = "北京";
root["address"] = address;
Json::StreamWriterBuilder writerBuilder;
writerBuilder["indentation"] = " ";
std::unique_ptr<Json::StreamWriter> writer(writerBuilder.newStreamWriter());
std::ostringstream oss;
writer->write(root, &oss);
std::string jsonStr = oss.str();
std::cout << "序列化结果:\n" << jsonStr << std::endl;
return 0;
}
除了 indentation,你还可以通过 writerBuilder 添加这些常用配置:
方式 1:便捷函数 writeString(推荐日常使用)
核心特点一行代码完成序列化,是 StreamWriter 的高级封装
可配置缩进(紧凑 / 格式化输出)
直接返回 std::string,无需手动操作流
适用场景绝大多数日常场景(接口请求、数据存储)
需要灵活配置缩进格式的场景
单次 / 少量序列化操作(无需复用写入器)
方式 2:手动创建 StreamWriter(灵活输出控制)
核心特点底层用法,手动管理 StreamWriter 生命周期
可复用写入器(多次序列化更高效)
支持输出到任意 std::ostream(控制台、文件、网络流)
适用场景多次序列化(循环中复用写入器,减少创建开销)
需要输出到文件 / 网络流等非字符串目标
需精细控制输出过程的场景
方式 3:toStyledString(极简调试用)
核心特点零配置,一行代码完成固定格式化输出(带缩进 / 换行,不可修改)
底层封装了默认的 StreamWriter
适用场景开发调试、日志打印
临时查看 JSON 结构(无需配置格式)
不推荐生产环境(固定格式,无法紧凑输出)
方式 4:自定义 StreamWriter(高级定制)
核心特点继承 Json::StreamWriter 重写序列化逻辑
支持特殊规则(如自定义布尔值、日期格式)
极致灵活,但开发成本高
适用场景需要自定义序列化规则(如特殊字符转义、非标格式)
对接特殊第三方接口(要求非标准 JSON 格式)
非必要不使用(增加维护成本)
(六)反序列化(JSON 字符串 → Value)
步骤配置 Json::CharReaderBuilder(可选,默认宽松解析);调用 parse() 解析字符串到 Json::Value;安全取值(先判断类型,再转换)。
#include <json/json.h>
#include <iostream>
#include <string>
int main() {
std::string jsonStr = R"({ "name": "张三", "age": 25, "hobbies": ["篮球", "编程"], "address": {"city": "北京"} })";
Json::CharReaderBuilder readerBuilder;
readerBuilder["rejectDupKeys"] = true;
std::unique_ptr<Json::CharReader> reader(readerBuilder.newCharReader());
Json::Value root;
std::string errs;
bool parseOk = reader->parse(
jsonStr.c_str(),
jsonStr.c_str() + jsonStr.size(),
&root, &errs
);
if (!parseOk)
{
std::cerr << "解析失败:" << errs << std::endl;
return 1;
}
std::cout << "姓名:" << root.get("name", "未知").asString() << std::endl;
std::cout << "年龄:" << root.get("age", 0).asInt() << std::endl;
if (root["hobbies"].isArray())
{
std::cout << "爱好:";
for (const auto& hobby : root["hobbies"])
{
std::cout << hobby.asString() << " ";
}
std::cout << std::endl;
}
if (root["address"].isObject())
{
std::cout << "城市:" << root["address"]["city"].asString() << std::endl;
}
return 0;
}
几种常见的反序列化的解析方式:
1. parseFromStream(主流)
核心特点:基于 std::istream 设计,支持字符串流、文件流、网络流等任意输入源
可通过 CharReaderBuilder 配置解析规则(如注释支持、精度控制)
线程安全、扩展性强
适用场景:新项目首选(兼容所有 JsonCpp 新版本)
处理大文件 / 流式数据(无需一次性加载全部内容)
需要自定义解析规则的场景
2. fromString(简单)
核心特点:parseFromStream 的封装版,直接接收字符串,无需手动创建流
零配置快速解析,牺牲部分灵活性换取简洁性
适用场景:简单场景(无需配置解析规则)
快速调试、小体积 JSON 字符串解析
3. Json::Reader::parse(旧版 API)
核心特点:早期 JsonCpp 主流方式,现已被标记为「过时」
直接操作字符串指针,C 风格明显
配置选项少,非线程安全
适用场景:维护老旧项目(无法升级 JsonCpp 版本)
临时兼容历史代码(建议逐步替换为新 API)
4. 自定义 CharReader(高级解析)
核心特点:继承 Json::CharReader 重写解析逻辑
支持非标 JSON 格式(如自定义注释、特殊字符)
开发成本高,仅适用于极特殊场景
建议:新项目优先使用 parseFromStream(兼顾灵活性和性能);简单场景可使用 FromString 简化代码;
(七)JSON 文件的读写操作
实际开发中,我们经常需要从 JSON 文件读取配置,或将 JSON 数据写入文件持久化,这里给出完整的文件读写实现。
#include <iostream>
#include <fstream>
#include <memory>
#include <jsoncpp/json/json.h>
bool readJsonFromFile(const std::string& file_path, Json::Value& root) {
std::ifstream ifs(file_path, std::ios::in | std::ios::binary);
if (!ifs.is_open())
{
std::cerr << "文件打开失败:" << file_path << std::endl;
return false;
}
std::string json_str((std::istreambuf_iterator<char>(ifs)), std::istreambuf_iterator<char>());
ifs.close();
Json::CharReaderBuilder crb;
crb.settings_["allowComments"] = true;
std::unique_ptr<Json::CharReader> pcr(crb.newCharReader());
std::string err_msg;
bool ret = pcr->parse(json_str.data(), json_str.data() + json_str.size(), &root, &err_msg);
if (!ret)
{
std::cerr << "JSON 解析失败:" << err_msg << std::endl;
return false;
}
std::cout << "JSON 文件读取解析成功" << std::endl;
return true;
}
bool writeJsonToFile(const std::string& file_path, const Json::Value& root) {
Json::StreamWriterBuilder swb;
swb.settings_["indentation"] = " ";
swb.settings_["emitUTF8"] = true;
std::unique_ptr<Json::StreamWriter> psw(swb.newStreamWriter());
std::ofstream ofs(file_path, std::ios::out | std::ios::binary | std::ios::trunc);
if (!ofs.is_open())
{
std::cerr << "文件打开失败:" << file_path << std::endl;
return false;
}
int ret = psw->write(root, &ofs);
ofs.close();
if (ret != 0)
{
std::cerr << "JSON 序列化写入失败" << std::endl;
return false;
}
std::cout << "JSON 文件写入成功" << std::endl;
return true;
}
int main() {
Json::Value config;
if (!readJsonFromFile("./config.json", config))
{
return -1;
}
config["version"] = "1.0.1";
config["update_time"] = "2024-01-01";
config["server"]["port"] = 8080;
if (!writeJsonToFile("./new_config.json", config))
{
return -1;
}
return 0;
}
五、避坑指南
6.1 坑点与解决方案
坑 1:operator [] 的副作用(最常见)
问题:使用非 const 的 Json::Value 的 operator[] 访问不存在的键时,会自动向对象中插入该键,值为 null,导致原对象被意外修改。
Json::Value obj;
if (obj["not_exist_key"].isNull())
{
std::cout << "键不存在" << std::endl;
}
- 先通过
isMember() 判断键是否存在,再访问值
- 使用 const 引用访问,const 版本的
operator[] 不会修改原对象,键不存在时返回静态 null 值
- 使用
get(const char* key, const Value& defaultValue) 方法,无副作用,支持默认值
坑 2:类型不匹配导致的程序崩溃
问题:未判断类型直接调用 asXXX() 方法,类型不匹配时会直接抛出 Json::Exception 异常,未捕获时会导致程序崩溃。
解决方案:严格遵循「先判断类型,再转换取值」的流程,所有类型转换前必须做类型校验。
坑 3:数组越界自动扩容
问题:使用 operator[] 访问数组下标超过数组长度时,会自动将数组扩容到该下标 + 1 的大小,扩容部分填充 null 值,导致数组意外变大。
解决方案:访问数组前先判断下标是否小于数组 size(),禁止越界访问。
坑 4:带注释的 JSON 解析失败
问题:JSON 字符串中包含 // 或 /* */ 注释时,默认解析会直接失败。
解决方案:显式开启注释支持:crb.settings_["allowComments"] = true;
坑 5:字符串编码乱码
问题:传入 GBK、GB2312 等非 UTF-8 编码的字符串,序列化 / 反序列化后出现乱码。
解决方案:JsonCpp 原生仅支持 UTF-8 编码,所有字符串必须转换为 UTF-8 格式后再传入。
坑 6:线程安全问题
问题:多线程同时读写同一个 Json::Value 对象,出现数据竞争、程序崩溃。
解决方案:Json::Value 不是线程安全的,多线程环境下,读写同一个对象必须加互斥锁保护。
6.2 编码建议
- const 优先:访问 JSON 数据时,优先使用 const 引用,避免
operator[] 的副作用,同时提升性能
- 异常捕获:所有 JsonCpp 操作都建议包裹在 try-catch 中,捕获
Json::Exception 异常,避免程序意外崩溃
- 结构化封装:复杂 JSON 结构封装为 C++ 结构体,提供对应的序列化 / 反序列化方法,提升代码可维护性
- 内存优化:大数据量 JSON 处理时,使用
swap() 方法避免深拷贝,使用 resize() 预分配数组空间
- 网络传输优化:序列化网络传输的 JSON 时,关闭缩进(
indentation="")生成压缩格式,减少数据体积
- 调试优化:调试时使用
toStyledString() 快速生成格式化 JSON,定位数据问题
六、JsonCpp 与其他 C++ JSON 库对比
C++ 生态中还有多个优秀的 JSON 库,这里做横向对比
| 库名称 | 核心特点 | 优势 | 劣势 | 适用场景 |
|---|
| JsonCpp | 经典全功能库,非 header-only | 兼容性极强,支持老 C++ 标准,稳定成熟,工业级验证 | 性能中等,现代特性支持不如新库 | 老项目维护、需要兼容 C++03、企业级稳定优先的项目 |
| nlohmann/json | 现代 C++ header-only 库 | 用法极简,STL 兼容,功能丰富,header-only 无需编译 | 编译耗时较长,大数据量性能一般 | 新项目快速开发、追求开发效率、无需极致性能的场景 |
| RapidJSON | 腾讯开源的极致性能库 | 性能顶尖,内存占用极低,全功能支持 | API 较复杂,学习成本高,报错不友好 | 高频大数据量 JSON 处理、性能敏感的场景(如游戏、高并发服务) |
相关免费在线工具
- Base64 字符串编码/解码
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
- Base64 文件转换器
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
- Markdown转HTML
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
- HTML转Markdown
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online
- JSON 压缩
通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online
- JSON美化和格式化
将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online