news 2026/8/27 8:44:12

jsoncpp的编译和使用

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
jsoncpp的编译和使用

在前面的文章中为大家介绍了C语言中如何使用cjson处理json数据,接下来讲一下json在C++中的处理,这里给大家介绍一个开源的库jsoncpp。

1. 下载和编译

1.1 下载

下载 jsoncpp
Jsoncpp是个跨平台的C++开源库,提供的类为我们提供了很便捷的操作,而且使用的人也很多。在使用之前我们首先要从github仓库下载源码,地址如下:

GitHub - open-source-parsers/jsoncpp: A C++ library for interacting with JSON. · GitHubA C++ library for interacting with JSON. Contribute to open-source-parsers/jsoncpp development by creating an account on GitHub.https://github.com/open-source-parsers/jsoncpp

在命令控制台中以管理员身份运行,输入以下命令直接下载

D: mkdir WorkSpace cd WorkSpace git clone https://github.com/open-source-parsers/jsoncpp.git

下载 cmake 工具
由于C++程序员都是基于VS进行项目开发,下载的源码我们一般不会直接使用,而是将其编译成相应的库文件(动态库或者静态库),这样不论是从使用或者部署的角度来说,操作起来都会更方便一些。

但是,从github下载的源码不能直接通过VS打开,编译就更谈不上了。它提供的默认编译方式是cmake。我们可以通过使用cmake工具将下载的jsoncpp源码生成一个VS项目,这样就可以通过VS编译出需要的库文件了。

CMake工具的官方下载地址如下:

Download CMakehttps://cmake.org/download/

下载windows x64 installer版本

1.2 生成VS项目

我的安装路径

打开安装好的CMake工具

需要在工具中指定本地的jsoncpp路径(git clone 之后就会得到这个目录),这是我本地的目录:

第二个需要指定的是一个文件存储路径(生成的VS项目会保存到这个目录下),保证这是一个本地的有效目录即可。

选中右键点击生成

lib是对dll的简介

新建一个lib文件夹,把lib和dll都放进去

2. jsoncpp 的使用

jsoncpp库中的类被定义到了一个Json命名空间中,建议在使用这个库的时候先声明这个命名空间:

using namespace Json;

使用jsoncpp库解析json格式的数据,我们只需要掌握三个类:

1.Value 类:将json支持的数据类型进行了包装,最终得到一个Value类型
2.FastWriter类:将Value对象中的数据序列化为字符串
3.Reader类:反序列化, 将json字符串 解析成 Value 类型

2.1 Value类

这个类可以看做是一个包装器,它可以封装Json支持的所有类型,这样我们在处理数据的时候就方便多了。

构造函数
Value类为我们提供了很多构造函数,通过构造函数来封装数据,最终得到一个统一的类型。

// 因为Json::Value已经实现了各种数据类型的构造函数 Value(ValueType type = nullValue); Value(Int value); Value(UInt value); Value(Int64 value); Value(UInt64 value); Value(double value); Value(const char* value); Value(const char* begin, const char* end); Value(bool value); Value(const Value& other); Value(Value&& other);

检测保存的数据类型

// 检测保存的数据类型 bool isNull() const; bool isBool() const; bool isInt() const; bool isInt64() const; bool isUInt() const; bool isUInt64() const; bool isIntegral() const; bool isDouble() const; bool isNumeric() const; bool isString() const; bool isArray() const; bool isObject() const;

将Value对象转换为实际类型

Int asInt() const; UInt asUInt() const; Int64 asInt64() const; UInt64 asUInt64() const; LargestInt asLargestInt() const; LargestUInt asLargestUInt() const; JSONCPP_STRING asString() const; float asFloat() const; double asDouble() const; bool asBool() const; const char* asCString() const;

对json数组的操作

ArrayIndex size() const; Value& operator[](ArrayIndex index); Value& operator[](int index); const Value& operator[](ArrayIndex index) const; const Value& operator[](int index) const; // 根据下标的index返回这个位置的value值 // 如果没找到这个index对应的value, 返回第二个参数defaultValue Value get(ArrayIndex index, const Value& defaultValue) const; Value& append(const Value& value); const_iterator begin() const; const_iterator end() const; iterator begin(); iterator end();

对json对象的操作

Value& operator[](const char* key); const Value& operator[](const char* key) const; Value& operator[](const JSONCPP_STRING& key); const Value& operator[](const JSONCPP_STRING& key) const; Value& operator[](const StaticString& key); // 通过key, 得到value值 Value get(const char* key, const Value& defaultValue) const; Value get(const JSONCPP_STRING& key, const Value& defaultValue) const; Value get(const CppTL::ConstString& key, const Value& defaultValue) const; // 得到对象中所有的键值 typedef std::vector<std::string> Members; Members getMemberNames() const;

将Value对象数据序列化为string

// 序列化得到的字符串有样式 -> 带换行 -> 方便阅读 // 写配置文件的时候 std::string toStyledString() const;

2.2 FastWriter 类

// 将数据序列化 -> 单行 // 进行数据的网络传输 std::string Json::FastWriter::write(const Value& root);

2.3 Reader 类

bool Json::Reader::parse(const std::string& document, Value& root, bool collectComments = true); 参数: - document: json格式字符串 - root: 传出参数, 存储了json字符串中解析出的数据 - collectComments: 是否保存json字符串中的注释信息 // 通过begindoc和enddoc指针定位一个json字符串 // 这个字符串可以是完成的json字符串, 也可以是部分json字符串 bool Json::Reader::parse(const char* beginDoc, const char* endDoc, Value& root, bool collectComments = true); // write的文件流 -> ofstream // read的文件流 -> ifstream // 假设要解析的json数据在磁盘文件中 // is流对象指向一个磁盘文件, 读操作 bool Json::Reader::parse(std::istream& is, Value& root, bool collectComments = true);

3. VS的配置

如果想要在VS中使用编译出的jsoncpp库,我们还需要做如下配置:

3.1 头文件

在编码过程中需要在项目文件中包含从github下载得到的头文件,有两种处理方式:

1.将头文件放到项目目录下,直接被项目包含引用

2.将头文件放到一个本地固定目录,以后就不再动了,在VS项目属性中设置包含这个目录,我推荐这种

另外,在这个include目录中还有一个json子目录,所有的头文件都在这个子目录中,我们不要破坏这个目录结构:

在包含需要的头文件的时候,使用如下这种方式:

#include <json/json.h>

把本地的头文件目录在项目属性窗口中进行配置:

我这里头文件是放到了C盘的jsoncpp目录中:

3.2 库文件

我这里也是将生成的jsoncpp.lib和jsoncpp.dll放到了C盘(C:\jsoncpp\lib),在VS项目中需要指定这个库路径:

另外,还需要告诉VS需要加载的动态库是哪一个

此处指定的是动态库对应的lib文件,也就是jsoncpp.lib

配置完成之后,如果项目中使用了jsoncpp就可以编译通过了。在程序执行的时候,如果提示找不到jsoncpp的动态库,把 jsoncpp.dll 拷贝到可执行所在的目录下就可以解决这个问题了。

注意:如果提示“由于找不到jsoncpp.dll,无法继续执行代码”,可以把jsoncpp.dll动态库文件放到可执行程序的同级目录下

3. 示例代码

比如:我们要将下面这个Json数组写入的一个文件中

[ 12, 12.34, true, "tom", ["jack", "ace", "robin"], {"sex":"man", "girlfriend":"lucy"} ]
#include <json/json.h> #include <fstream> using namespace Json; int main() { writeJson(); readJson(); }

3.1 写json文件

void writeJson() { // 将最外层的数组看做一个Value // 最外层的Value对象创建 Value root; // Value有一个参数为int 行的构造函数 root.append(12); // 参数进行隐式类型转换 root.append(12.34); root.append(true); root.append("tom"); // 创建并初始化一个子数组 Value subArray; subArray.append("jack"); subArray.append("ace"); subArray.append("robin"); root.append(subArray); // 创建并初始化子对象 Value subObj; subObj["sex"] = "woman"; // 添加键值对 subObj["girlfriend"] = "lucy"; root.append(subObj); // 序列化 #if 1 // 有格式的字符串 string str = root.toStyledString(); #else FastWriter f; string str = f.write(root); #endif // 将序列化的字符串写磁盘文件 ofstream ofs("test.json"); ofs << str; ofs.close(); }

3.2 读json文件

void readJson() { // 1. 将磁盘文件中的json字符串读到磁盘文件 ifstream ifs("test.json"); // 2. 反序列化 -> value对象 Value root; Reader r; r.parse(ifs, root); // 3. 从value对象中将数据依次读出 if (root.isArray()) { // 数组, 遍历数组 for (int i = 0; i < root.size(); ++i) { // 依次取出各个元素, 类型是value类型 Value item = root[i]; // 判断item中存储的数据的类型 if (item.isString()) { cout << item.asString() << ", "; } else if (item.isInt()) { cout << item.asInt() << ", "; } else if (item.isBool()) { cout << item.asBool() << ", "; } else if (item.isDouble()) { cout << item.asFloat() << ", "; } else if (item.isArray()) { for (int j = 0; j < item.size(); ++j) { cout << item[j].asString() << ", "; } } else if (item.isObject()) { // 对象 // 得到所有的key Value::Members keys = item.getMemberNames(); for (int k = 0; k < keys.size(); ++k) { cout << keys.at(k) << ":" << item[keys[k]] << ", "; } } } cout << endl; } }

在上面读Json文件的这段代码中,对读出的每个Value类型的节点进行了类型判断,其实一般情况下是不需要做这样的判断的,因为我们在解析的时候是明确地知道该节点的类型的。

虽然Json这种格式无外乎数组和对象两种,但是需求不同我们设计的Json文件的组织方式也不同,一般都是特定的文件对应特定的解析函数,一个解析函数可以解析任何的Json文件这种设计思路是坚决不推荐的。

笔记

// ============================================================================ // 一、operator[] 重载(通过键名访问值) // ============================================================================ // ---- 非 const 版本:使用 C 风格字符串作为键名 ---- // 功能:通过键名获取对应的 Value 引用,可以修改这个值 // 参数:key - C 风格字符串(以 '\0' 结尾的 char 数组) // 返回:Value& 引用,可以直接赋值或修改 // 注意:如果键不存在,会创建一个空的 Value,所以读取前最好先判断 Value& operator[](const char* key); // ---- const 版本:使用 C 风格字符串作为键名 ---- // 功能:通过键名获取对应的 Value 引用,只能读取不能修改 // 参数:key - C 风格字符串 // 返回:const Value& 常量引用,只能读取 // 注意:这个版本不会创建新键,适合在 const 函数中使用 const Value& operator[](const char* key) const; // ---- 非 const 版本:使用 C++ 字符串作为键名 ---- // 功能:同上,但参数是 std::string 类型 // JSONCPP_STRING 是 std::string 的别名(为了跨平台兼容) // 使用场景:当键名本身来自 std::string 变量时 Value& operator[](const JSONCPP_STRING& key); // ---- const 版本:使用 C++ 字符串作为键名 ---- // 功能:同上,const 版本,只能读取 const Value& operator[](const JSONCPP_STRING& key) const; // ---- 非 const 版本:使用静态字符串作为键名 ---- // 功能:同上,但参数是 StaticString 类型 // StaticString 是一个轻量级包装,不会拷贝字符串,性能更好 // 使用场景:键名是字符串字面量,且多次使用时 // 例如:root[StaticString("name")] = "张三"; Value& operator[](const StaticString& key); // ============================================================================ // 二、get() 方法(安全地获取值,带默认值) // ============================================================================ // ---- 使用 C 风格字符串作为键名 ---- // 功能:安全地获取键对应的值,如果键不存在则返回默认值 // 参数: // key - 要查找的键名 // defaultValue - 键不存在时返回的默认值 // 返回:Value 类型(副本),包含实际值或默认值 // // 与 operator[] 的区别: // operator[] 在键不存在时会创建一个空 Value // get() 在键不存在时返回你指定的默认值,更安全 Value get(const char* key, const Value& defaultValue) const; // ---- 使用 C++ 字符串作为键名 ---- // 功能:同上,参数是 std::string 类型 Value get(const JSONCPP_STRING& key, const Value& defaultValue) const; // ---- 使用 ConstString 作为键名 ---- // 功能:同上,参数是 CppTL::ConstString 类型 // CppTL::ConstString 是另一种静态字符串包装,用于性能优化 Value get(const CppTL::ConstString& key, const Value& defaultValue) const; // ============================================================================ // 三、getMemberNames() 方法(获取所有键名) // ============================================================================ // ---- 定义 Members 类型别名 ---- // Members 是 std::vector<std::string> 的别名 // 用于存储 JSON 对象中所有键名的集合 typedef std::vector<std::string> Members; // ---- 获取所有键名 ---- // 功能:获取当前 JSON 对象中所有的键名 // 返回:std::vector<std::string>,包含所有键名 // // 使用场景: // 1. 遍历整个 JSON 对象 // 2. 检查对象中有哪些字段 // 3. 动态处理未知结构的 JSON // // 注意:返回的键名顺序不固定(JSON 标准不保证顺序) Members getMemberNames() const;

// ============================================================================ // 包含头文件 // ============================================================================ // 标准输入输出流,用于控制台输出 #include <iostream> // JSONCPP 核心库,提供 JSON 的读写操作 #include <json/json.h> // 文件流,用于读写文件 #include <fstream> // 使用标准命名空间(简化 std:: 前缀) using namespace std; // 使用 JSONCPP 命名空间(简化 Json:: 前缀) using namespace Json; // ============================================================================ // writeJson() 函数:创建并写入 JSON 文件 // ============================================================================ void writeJson() { // ---- 1. 创建根节点 ---- // Value 是 JSONCPP 的核心类型,可以表示任何 JSON 数据类型 // 默认构造的 Value 是 null 类型 Value root; // ---- 2. 添加数据到数组 ---- // append() 方法向数组中添加元素(自动分配索引) // root 现在是一个数组:[] // 添加字符串 "luffy" root.append("luffy"); // 添加整数 19 root.append(19); // 添加整数 170 root.append(170); // 添加布尔值 false root.append(false); // 此时 root = ["luffy", 19, 170, false] // ---- 3. 创建子数组 ---- Value subArray; // 向子数组中添加字符串 subArray.append("ace"); subArray.append("sabo"); // 将子数组添加到根数组中 // 此时 root = ["luffy", 19, 170, false, ["ace", "sabo"]] root.append(subArray); // ---- 4. 创建对象 ---- Value obj; // 使用 operator[] 添加键值对(类似字典) obj["sex"] = "man"; // 键 "sex",值 "man" obj["girlfriend"] = "Hancock"; // 键 "girlfriend",值 "Hancock" // 将对象添加到根数组中 // 此时 root = ["luffy", 19, 170, false, ["ace", "sabo"], {"sex":"man", "girlfriend":"Hancock"}] root.append(obj); // ---- 5. 选择输出格式 ---- #if 0 // 条件编译:0 表示不编译这段代码 // toStyledString() 输出格式化(带缩进、换行),便于阅读 string json = root.toStyledString(); #else // 1 表示编译这段代码 // FastWriter 输出紧凑格式(无空格、无换行),节省空间 // 注意:FastWriter 在 JSONCPP 新版本中已被标记为废弃,推荐使用 StreamWriterBuilder FastWriter w; string json = w.write(root); #endif // ---- 6. 写入文件 ---- // ofstream:输出文件流,打开 "test.json" 文件 // 如果文件不存在,会自动创建;如果存在,会覆盖 ofstream ofs("test.json"); // 将 JSON 字符串写入文件 ofs << json; // 关闭文件流(释放资源) ofs.close(); } // ============================================================================ // readJson() 函数:读取并解析 JSON 文件 // ============================================================================ void readJson() { // ---- 1. 打开文件 ---- // ifstream:输入文件流,打开 "test.json" 文件 ifstream ifs("test.json"); // ---- 2. 创建解析器和根节点 ---- // Reader:JSON 解析器,将 JSON 字符串转换为 Value 对象 // 注意:Reader 在 JSONCPP 新版本中已被标记为废弃,推荐使用 CharReaderBuilder Reader rd; // 根节点:存储解析后的 JSON 数据 Value root; // ---- 3. 解析 JSON ---- // parse() 方法:从文件流中读取并解析 JSON // 参数:输入流,输出根节点 // 返回值:true 表示解析成功,false 表示失败 rd.parse(ifs, root); // ---- 4. 检查根节点是否为数组 ---- // isArray() 判断 Value 是否为 JSON 数组类型 if (root.isArray()) { // ---- 5. 遍历数组 ---- // size() 返回数组的长度 for (unsigned i = 0; i < root.size(); i++) { // 获取数组中的第 i 个元素 Value item = root[i]; // ---- 6. 根据元素类型分别处理 ---- // 判断是否为字符串 if (item.isString()) { // asString() 将 Value 转换为 std::string cout << item.asString() << ", "; } // 判断是否为整数 else if (item.isInt()) { // asInt() 将 Value 转换为 int cout << item.asInt() << ", "; } // 判断是否为布尔值 else if (item.isBool()) { // asBool() 将 Value 转换为 bool // 输出时 bool 会显示为 0 或 1 cout << item.asBool() << ", "; } // 判断是否为浮点数(double) else if (item.isDouble()) { // asFloat() 将 Value 转换为 float cout << item.asFloat() << ", "; } // 判断是否为数组(嵌套数组) else if (item.isArray()) { // 遍历子数组 for (unsigned j = 0; j < item.size(); ++j) { // 子数组中的元素都当成字符串处理 cout << item[j].asString() << ", "; } } // 判断是否为对象(键值对) else if (item.isObject()) { // ---- 7. 处理对象 ---- // getMemberNames() 返回所有键名的集合 // Value::Members 是 std::vector<std::string> 的别名 Value::Members keys = item.getMemberNames(); // 遍历所有键 for (int k = 0; k < keys.size(); ++k) { // keys.at(k) 获取第 k 个键名(std::string) // item[keys[k]] 通过键名获取对应的值 // 输出格式:key:value cout << keys.at(k) << ":" << item[keys[k]] << ", "; } } // 每处理完一个元素,换行 cout << endl; } } } // ============================================================================ // main() 函数:程序入口 // ============================================================================ int main() { // ---- 1. 写入 JSON 文件 ---- // 调用 writeJson(),生成 test.json 文件 writeJson(); // ---- 2. 读取并解析 JSON 文件 ---- // 调用 readJson(),读取 test.json 并打印到控制台 readJson(); // 程序正常结束 return 0; }
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/27 8:43:16

ROS2机器人实战:从环境搭建到SLAM建图与Nav2自主导航

如果你正准备从零上手 ROS2 机器人开发&#xff0c;而且想一次把传感器、仿真、SLAM 建图和自主导航跑通&#xff0c;这篇文章建议直接收藏。标题里这套 ROS2 机器人实战教程覆盖的链路很完整&#xff1a;先装环境&#xff0c;再接传感器&#xff0c;然后在 Gazebo 里做仿真验证…

作者头像 李华
网站建设 2026/8/27 8:43:15

Java File

介绍:File对象就表示一个路径,可以是文件的路径、也可以是文件夹的路径这个路径可以是存在的,也可以是不存在的绝对路径于相对路径绝对路径:带盘符的路径相对路径:不带盘符,默认到当前项目下去找File的构造方法方法名称说明public File(String pathname)根据文件路径创建文件对…

作者头像 李华
网站建设 2026/8/27 8:39:30

C++11核心特性深度解析:从列表初始化到可变参数模板的现代编程实践

1. 项目概述&#xff1a;C11新语法特性深度解析 作为一名在C领域摸爬滚打了十多年的老码农&#xff0c;我至今还记得第一次接触C11标准时那种“豁然开朗”的感觉。那感觉就像是&#xff0c;你一直用着一把功能齐全但略显笨重的瑞士军刀&#xff0c;突然有人给你升级了一套模块化…

作者头像 李华
网站建设 2026/8/27 8:39:11

Python通达信数据接口 MOOTDX:三步拉通 A股K线数据

Python通达信数据接口 MOOTDX&#xff1a;三步拉通 A股K线数据 【免费下载链接】mootdx 通达信数据读取的一个简便使用封装 项目地址: https://gitcode.com/GitHub_Trending/mo/mootdx 晚上复盘想批量拉沪深两市的日线&#xff0c;商业数据源按月收费&#xff0c;免费接…

作者头像 李华
网站建设 2026/8/27 8:36:50

自制多功能DDS信号发生器:从原理到实战全流程解析

工具柜里常年放着好几台测试仪器的人&#xff0c;大概率都经历过同一个尴尬时刻&#xff1a;测音频电路想扫个频响&#xff0c;手边只有固定输出的电源&#xff1b;调数字板卡需要逻辑脉冲&#xff0c;翻出来的函数发生器又只能出正弦&#xff1b;好不容易找到一台能调波形的&a…

作者头像 李华