ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

jsoncpp的编译和使用

jsoncpp的编译和使用 在前面的文章中为大家介绍了C语言中如何使用cjson处理json数据接下来讲一下json在C中的处理这里给大家介绍一个开源的库jsoncpp。1. 下载和编译1.1 下载下载 jsoncppJsoncpp是个跨平台的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::vectorstd::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::vectorstd::string 的别名 // 用于存储 JSON 对象中所有键名的集合 typedef std::vectorstd::string Members; // ---- 获取所有键名 ---- // 功能获取当前 JSON 对象中所有的键名 // 返回std::vectorstd::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. 创建解析器和根节点 ---- // ReaderJSON 解析器将 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::vectorstd::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; }
返回列表