Json 概述: https://xingzhu.top/archives/json-gai-shu
C++中原生不支持Json
,所以需要导入Jsoncpp
库
准备环境
- 下载
Jsoncpp
从 github 上下载源码
# 这是链接地址
https://github.com/open-source-parsers/jsoncpp
这里我使用的是 git
工具克隆了一份数据下来
git clone git@github.com:open-source-parsers/jsoncpp.git
- 下载 Cmake 工具
- 这个工具是为了编译这个 Jsoncpp 源码编译成动态库,然后需要使用的时候直接使用这个库即可
- 否则需要把这些源码文件和项目文件放一起编译,这样不利于维护,且显得体量过大
VS 环境下配置
- 由于需要在 VS 上写代码,所以需要将源码编译成 VS 环境下的动态库文件
- 因此这里演示如何配置
编译
- 打开 cmake 工具,然后选择对于的文件路径,第一个路径为下载 Jsoncpp 源码后的路径,第二个路径为编译后文件的路径
- 选择好路径后,点击左下角的
configure
按钮,然后弹出对话框,然后选择对于的 VS 版本,也就是自己本地的版本,我的是 VS2022,就选择了这个,其他选项默认即可
点击
Finish
后,等待几秒,点击Generate
生成 VS 项目,生成后,去对应的路径点击jsoncpp.sln
打开这个项目找到
jsoncpp_lib
,右击选择生成,此时就能得到我们想要的库文件了
使用
- 由于需要在编写代码时候需要
jsoncpp
对应的头文件,因此配置头文件,有两种方式
- 将头文件放到项目目录下,直接被项目包含引用
- 将头文件放到一个本地固定目录,以后就不再动了,在 VS 项目属性中设置包含这个目录
这里选择第二种
将源码 jsoncpp
中的 include
目录拷贝到本地,注意是源码中的 include
目录,不是生成的 VS 项目中的 include
目录
由于这个 include
目录下有个 json
目录,并且这个目录中才是存放的头文件,所以访问里面的头文件时需要这样
#include <json/json.h>
不建议破坏整个目录结构,存在隐患
- 还要将对应的动态库也放在本地固定目录
一个是前面步骤中生成的 vs 项目的
bin
目录下的Debug
目录中的jsoncpp.dll
一个是前面步骤中生成的 vs 项目的
lib
目录下的Debug
目录中的jsoncpp.lib
将这两个文件复制到之前 include
同级目录下,新建一个 lib
目录,放在这里面
- 接下来就是在 VS 中配置了
点击小扳手,打开项目属性
指定头文件的路径:点击这个,然后编辑包含目录
填写刚刚拷贝 include
的固定目录路径位置
指定库路径:再编辑库目录
将刚刚拷贝动态库和导入库到的固定目录路径填写进去
还需要告诉 VS 需要加载的动态库是哪一个
填写导入库的名称,也就是 jsoncpp.lib
到这里就配置完成了,如果执行报错不能找到动态库
jsoncpp.dll
只需要将这个文件拷贝到当前 VS 测试项目代码位置即可
测试代码:
#include <iostream>
#include <json/json.h>
using namespace Json;
using namespace std;
int main()
{
Value root;
root["message"] = "Hello, JsonCpp!";
cout << root.toStyledString() << std::endl;
return 0;
}
Jsoncpp 的使用
jsoncpp
库中的类被定义到了一个 Json 命名空间中
using namespace Json;
使用 jsoncpp
库解析 json
格式的数据,我们只需要掌握三个类:
Value
类:将json
支持的数据类型进行了包装,最终得到一个Value
类型FastWriter
类:将Value
对象中的数据序列化为字符串Reader
类:反序列化,将json
字符串解析成Value
类型
Value
// 判断保存的类型是否是 string / 数组 / 对象
// 这里这列举了三个,还有其他数据类型,如 int, double....
bool isString() const;
bool isArray() const;
bool isObject() const;
// 将 Value 对象转换为实际类型
Int asInt() const;
JSONCPP_STRING asString() 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, 返回第二个参数d efaultValue
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);
Value& operator[](const JSONCPP_STRING& key);
Value& operator[](const StaticString& key);
const Value& operator[](const char* key) const;
const Value& operator[](const JSONCPP_STRING& key) const;
// 通过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;
FastWriter
// 将 Value 对象数据序列化为 string
// 序列化得到的字符串有样式 -> 带换行 -> 方便阅读 -> 适用于写配置文件的时候
std::string toStyledString() const;
// 将数据序列化 -> 单行 -> 适用于网络传输的时候
std::string Json::FastWriter::write(const Value& root);
Reader
// 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);
示例
准备的 Json 数据
[
12,
12.34,
true,
"tom",
["jack", "ace", "robin"],
{"sex":"man", "girlfriend":"lucy"}
]
写 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);
// 序列化
// 有格式的字符串
string str = root.toStyledString();
// 一整行
// FastWriter f;
// string str = f.write(root);
// 将序列化的字符串写磁盘文件
ofstream ofs("test.json");
ofs << str;
ofs.close();
}
读 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
数据封装的数据格式 但是实际中,我们其实大多是知道这个数据格式的,也就能直接读取对应数据了
说明:参考学习:https://subingwen.cn/