JsonCpp超详细使用教程

一、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格式的字符串,用来输出或者发送。

核心套路:

  1. 创建 StreamWriterBuilder 工厂对象
  2. 用工厂 newStreamWriter() 创建写入器
  3. 调用 write() 方法把Value写进字符串流

3. CharReader ------ 反序列化(JSON字符串 → C++数据)

反序列化就是把JSON字符串解析成Json::Value对象,用来读取数据。

核心套路:

  1. 创建 CharReaderBuilder 工厂对象
  2. 用工厂 newCharReader() 创建读取器
  3. 调用 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的核心知识点我们梳理一下:

三大核心类:

  1. Json::Value ------ 万能容器,存各种JSON数据,用[]访问键值,append追加数组
  2. StreamWriterBuilder ------ 序列化工厂,把Value转成JSON字符串
  3. CharReaderBuilder ------ 反序列化工厂,把JSON字符串解析成Value

常用操作:

  • 取值:asString() / asInt() / asBool() / asDouble()
  • 判断:isNull() / isArray() / isObject() / isMember()
  • 对象:value["key"] 直接读写
  • 数组:append() / size() / value[i]

最佳实践:

  • 读取字段前先判断isNull(),避免访问不存在的字段
  • C++11以上用原始字符串R"()"写JSON,不用转义双引号
  • 用智能指针管理StreamWriter和CharReader,自动释放内存
相关推荐
Joey_friends1 小时前
指纹authenticate流程图
android·java·c++
小小晓.1 小时前
C++小白记:C风格字符串和数组用法
c语言·开发语言·c++
cpp_25012 小时前
P1540 [NOIP 2010 提高组] 机器翻译
数据结构·c++·算法·队列·noip·洛谷题解
咕白m6252 小时前
通过 C++ 写入数据到 Excel 文档
c++·后端
qz5zwangzihan13 小时前
题解:Atcoder Beginner Contest abc467 F - Email Scheduling Optimization
c++·贪心·atcoder·平衡树·abc467·abc467_f·fhq-treap
郝学胜-神的一滴4 小时前
中级OpenGL教程 023:Assimp模型加载全解——从源码到架构的骈文探秘
c++·unity·游戏引擎·cmake·unreal engine·opengl
星恒随风4 小时前
C++ STL 详解:list 的使用、迭代器失效、模拟实现与 vector 对比
开发语言·数据结构·c++·笔记·学习·list
牢姐与蒯4 小时前
c++之异常
开发语言·c++·c++11
此生决int4 小时前
深入理解C++系列(02)——类和对象(上)
开发语言·c++