一、JSON是什么
JSON全称 JavaScript Object Notation,是一种轻量级的数据交换格式。别看它名字里带JavaScript,它是完全独立于语言的,啥编程语言都能用。
JSON的基本结构
- 对象 :大括号
{}包起来,里面是键值对 - 数组 :方括号
[]包起来,是值的有序集合 - 值:可以是字符串、数字、布尔值(true/false)、null、对象、数组
语法规则
- 键值对格式:
"key": value,键必须用双引号 - 多个键值对之间用逗号
,分隔
来看个例子就懂了:
json
{
"name": "John Doe",
"age": 30,
"is_student": false,
"skills": ["JavaScript", "Python", "C++"],
"address": {
"street": "123 Main St",
"city": "Anytown",
"state": "CA"
}
}
JSON的用途非常广:数据交换、配置文件、接口返回值、数据存储,几乎无处不在。
二、JsonCpp是什么 & 安装
JsonCpp简介
JsonCpp是C++里最常用的JSON处理库,专门用来解析和生成JSON数据的。
它的特点:
- 简单好用:API非常直观,上手快
- 高性能:解析速度快,大数据量也扛得住
- 跨平台:Windows、Linux、macOS都支持
- 无依赖:不依赖其他外部库,集成方便
- 完全支持C++11:智能指针啥的都用上了
安装
Ubuntu一条命令搞定:
bash
sudo apt install libjsoncpp-dev
使用的时候记得包含头文件和链接库:
cpp
// 头文件
#include <jsoncpp/json/json.h>
bash
# 编译时链接库
g++ main.cc -o main -ljsoncpp
三、JsonCpp核心类拆解
JsonCpp的核心就三个东西,我们逐个拆分理解一下:
1. Json::Value ------ JSON数据的万能容器
这是JsonCpp最核心的类,不管是对象、数组、字符串、数字,统统都用Json::Value来表示。
JSON支持的数据类型:
cpp
enum ValueType {
nullValue = 0, // null空值
intValue, // 有符号整数
uintValue, // 无符号整数
realValue, // 浮点数double
stringValue, // 字符串
booleanValue, // 布尔值
arrayValue, // 数组
objectValue // 对象
};
取值方法(把Value转成C++类型):
cpp
asString() // 转字符串
asInt() // 转int
asInt64() // 转int64
asDouble() // 转double
asBool() // 转bool
asFloat() // 转float
判断类型方法:
cpp
isNull() // 是否为空
isBool() // 是否是布尔值
isInt() // 是否是整数
isString() // 是否是字符串
isArray() // 是否是数组
isObject() // 是否是对象
对象操作(键值对):
cpp
value["key"] = xxx; // 用[]直接赋值或取值
value["key"]; // 获取某个键的值
isMember("key") // 判断是否存在某个键
removeMember("key") // 删除某个键
数组操作:
cpp
size() // 获取数组元素个数
empty() // 数组是否为空
clear() // 清空数组
append(value) // 往数组末尾追加元素
value[i] // 用下标访问第i个元素
2. StreamWriter ------ 序列化(C++数据 → JSON字符串)
序列化就是把Json::Value对象转成JSON格式的字符串,用来输出或者发送。
核心套路:
- 创建
StreamWriterBuilder工厂对象 - 用工厂
newStreamWriter()创建写入器 - 调用
write()方法把Value写进字符串流
3. CharReader ------ 反序列化(JSON字符串 → C++数据)
反序列化就是把JSON字符串解析成Json::Value对象,用来读取数据。
核心套路:
- 创建
CharReaderBuilder工厂对象 - 用工厂
newCharReader()创建读取器 - 调用
parse()方法解析字符串到Value
四、序列化实战------生成JSON字符串
序列化就是把我们C++里的数据组织成JSON格式的字符串。话不多说直接上代码!
cpp
#include <iostream>
#include <sstream>
#include <jsoncpp/json/json.h>
int main(int argc, char* argv[])
{
// ========== 第一步:组织JSON数据 ==========
// 先创建一个子对象address
Json::Value item;
item["street"] = "123 Main St"; // 字符串
item["city"] = "Anytown";
item["state"] = "CA";
// 创建根对象
Json::Value value;
value["name"] = "John Doe"; // 字符串
value["age"] = 30; // 整数
value["is_student"] = false; // 布尔值
// 数组:用append逐个追加
value["skills"].append("JavaScript");
value["skills"].append("Python");
value["skills"].append("C++");
// 嵌套对象:直接把整个item赋值过去
value["address"] = item;
// ========== 第二步:序列化输出 ==========
// 1. 创建StreamWriter工厂对象
Json::StreamWriterBuilder swb;
// 可选配置:要不要格式化输出(缩进换行)
// swb.settings_["indentation"] = ""; // 设为空就是压缩成一行
// 2. 用工厂创建StreamWriter对象(智能指针自动释放)
std::unique_ptr<Json::StreamWriter> psw(swb.newStreamWriter());
// 3. 用字符串流接收结果
std::stringstream ss;
try {
// 序列化:把value写入ss
psw->write(value, &ss);
// 输出JSON字符串
std::cout << ss.str() << std::endl;
}
catch (Json::Exception &e) {
std::cout << "Error:" << e.what() << std::endl;
}
return 0;
}
运行结果
json
{
"address" :
{
"city" : "Anytown",
"state" : "CA",
"street" : "123 Main St"
},
"age" : 30,
"is_student" : false,
"name" : "John Doe",
"skills" :
[
"JavaScript",
"Python",
"C++"
]
}
五、反序列化实战------解析JSON字符串
反序列化就是把JSON字符串解析出来,读取里面的各个字段。这是实际开发中用得最多的场景!
cpp
#include <iostream>
#include <jsoncpp/json/json.h>
using namespace std;
int main(int argc, char* argv[])
{
// ========== 第一步:准备JSON字符串 ==========
// 用C++11的原始字符串R"(...)"写JSON超方便,不用转义双引号
string str = R"(
{
"address":{
"city":"Anytown",
"state":"CA",
"street":"123 Main St"
},
"age":30,
"is_student":false,
"name":"John Doe",
"skills":["JavaScript","Python","C++"]
}
)";
// ========== 第二步:反序列化解析 ==========
// 用来接收解析结果的Value对象
Json::Value value;
// 1. 创建CharReader工厂对象
Json::CharReaderBuilder build;
// 2. 用工厂创建CharReader对象
unique_ptr<Json::CharReader> pcr(build.newCharReader());
// 用来接收错误信息
string err;
// 3. 开始解析
// 参数:字符串起始地址、结束地址、结果接收、错误信息
bool ret = pcr->parse(&str[0], &str[str.size()], &value, &err);
if (ret == false) {
cout << "Error: " << err << endl;
return -1;
}
// ========== 第三步:读取解析后的数据 ==========
// 读取普通字段:先判断字段是否存在,再转对应类型
if (!value["name"].isNull())
cout << "name: " << value["name"].asString() << endl;
if (!value["age"].isNull())
cout << "age: " << value["age"].asInt() << endl;
if (!value["is_student"].isNull())
cout << "is_student: " << value["is_student"].asBool() << endl;
// 不存在的字段不会报错,isNull()返回true
if (!value["weight"].isNull())
cout << "weight: " << value["weight"].asFloat() << endl;
// 读取数组:先判断是不是数组,再用size()和下标遍历
if (!value["skills"].isNull() && value["skills"].isArray()) {
int sz = value["skills"].size(); // 获取数组长度
for (int i = 0; i < sz; i++) {
cout << "skills: " << value["skills"][i].asString() << endl;
}
}
// 读取嵌套对象:一层层[]往里取就行
if (!value["address"].isNull() && value["address"].isObject()) {
cout << "address.street: ";
cout << value["address"]["street"].asString() << endl;
cout << "address.city: ";
cout << value["address"]["city"].asString() << endl;
cout << "address.state: ";
cout << value["address"]["state"].asString() << endl;
}
return 0;
}
运行结果
name: John Doe
age: 30
is_student: 0
skills: JavaScript
skills: Python
skills: C++
address.street: 123 Main St
address.city: Anytown
address.state: CA
小提示:读取字段前最好先用isNull()判断一下存不存在,防止访问不存在的字段出问题,这是好习惯哦!
六、Makefile编译参考
makefile
all: serialize unserialize
serialize: serialize.cc
g++ -std=c++11 $^ -o $@ -ljsoncpp
unserialize: unserialize.cc
g++ -std=c++11 $^ -o $@ -ljsoncpp
make一下两个程序就都编译好了,非常方便。
总结
JsonCpp的核心知识点我们梳理一下:
三大核心类:
Json::Value------ 万能容器,存各种JSON数据,用[]访问键值,append追加数组StreamWriterBuilder------ 序列化工厂,把Value转成JSON字符串CharReaderBuilder------ 反序列化工厂,把JSON字符串解析成Value
常用操作:
- 取值:
asString() / asInt() / asBool() / asDouble() - 判断:
isNull() / isArray() / isObject() / isMember() - 对象:
value["key"]直接读写 - 数组:
append() / size() / value[i]
最佳实践:
- 读取字段前先判断
isNull(),避免访问不存在的字段 - C++11以上用原始字符串
R"()"写JSON,不用转义双引号 - 用智能指针管理StreamWriter和CharReader,自动释放内存