在前面的文章中为大家介绍了C语言中如何使用cjson处理json数据,接下来讲一下json
在C++中的处理,这里给大家介绍一个开源的库jsoncpp
。
- 下载和编译 {#1-下载和编译} ===================
1.1 下载 {#1-1-下载}
下载 jsoncpp {#下载-jsoncpp}
Jsoncpp是个跨平台的C++开源库,提供的类为我们提供了很便捷的操作,而且使用的人也很多。在使用之前我们首先要从github
仓库下载源码,地址如下:
|-----------|--------------------------------------------------------|
| 1
| https://github.com/open-source-parsers/jsoncpp
|
下载 cmake 工具 {#下载-cmake-工具}
由于C++程序猿都是基于VS进行项目开发,下载的源码我们一般不会直接使用,而且将其编译成相应的库文件(动态库或者静态库),这样不论是从使用或者部署的角度来说,操作起来都会更方便一些。
但是,从github
下载的源码不能直接通过VS打开
,编译就更谈不上了。它提供的默认编译方式是cmake
。我们可以通过使用cmake
工具将下载的jsoncpp
源码生成一个VS项目
,这样就可以通过VS
编译出需要的库文件了。
CMake工具的官方下载地址如下:
|-----------|-------------------------------------|
| 1
| https://cmake.org/download/
|
在这最新的安装包,根据向导完成安装即可。
1.2 生成VS项目 {#1-2-生成VS项目}
打开安装好的CMake
工具
需要在工具中指定本地的jsoncpp
路径(git clone 之后就会得到这个目录),这是我本地的目录:
第二个需要指定的是一个文件存储路径(生成的VS项目会保存到这个目录下),保证这是一个本地的有效目录即可。
设置好之后进行配置,点击Configure
按钮
此处需要设置一下,VS的版本以及生成器的平台位数,不填默认就是64位。
配置完成,开始生成VS项目。
打开在CMake
工具中指定的生成目录,我这里是D:\output-project
,基于项目文件jsoncpp.sln
打开这个VS项目。
1.3 编译 {#1-3-编译}
基于生成的项目文件打开VS项目之后,可以看到里边有很多子项目
我们只需要编译上图标记的那一个就可以了,编译成功之后就可以得到我们需要的库文件了。
通过输出的日志信息,就能找到我们想要的动态库了,把这两个文件收集起来备用。
- jsoncpp 的使用 {#2-jsoncpp-的使用} ===============================
jsoncpp
库中的类被定义到了一个Json
命名空间中,建议在使用这个库的时候先声明这个命名空间:
|-----------|-------------------------------|
| 1
| using namespace Json;
|
使用jsoncpp
库解析json
格式的数据,我们只需要掌握三个类:
Value 类
:将json支持的数据类型进行了包装,最终得到一个Value类型FastWriter类
:将Value对象中的数据序列化为字符串Reader类
:反序列化, 将json字符串 解析成 Value 类型
2.1 Value类 {#2-1-Value类}
这个类可以看做是一个包装器,它可以封装Json支持的所有类型,这样我们在处理数据的时候就方便多了。
| 枚举类型 | 说明 | 翻译 | |--------------|-----------------------------------------------|-------------------| | nullValue | 'null' value | 不表示任何数据,空值 | | intValue | signed integer value | 表示有符号整数 | | uintValue | unsigned integer value | 表示无符号整数 | | realValue | double value | 表示浮点数 | | stringValue | UTF-8 string value | 表示utf8格式的字符串 | | booleanValue | bool value | 表示布尔数 | | arrayValue | array value (ordered list) | 表示数组,即JSON串中的[] | | objectValue | object value (collection of name/value pairs) | 表示键值对,即JSON串中的{} |
构造函数 {#构造函数}
Value类为我们提供了很多构造函数,通过构造函数来封装数据,最终得到一个统一的类型。
|------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8 9 10 11 12
| // 因为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);
|
检测保存的数据类型 {#检测保存的数据类型}
|---------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8 9 10 11 12 13
| // 检测保存的数据类型 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对象转换为实际类型 {#将Value对象转换为实际类型}
|---------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8 9 10 11
| 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数组的操作 {#对json数组的操作}
|---------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8 9 10 11 12 13
| 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对象的操作 {#对json对象的操作}
|------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14
| 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 {#将Value对象数据序列化为string}
|---------------|----------------------------------------------------------------------------------------|
| 1 2 3
| // 序列化得到的字符串有样式 -> 带换行 -> 方便阅读 // 写配置文件的时候 std::string toStyledString() const;
|
2.2 FastWriter 类 {#2-2-FastWriter-类}
|---------------|----------------------------------------------------------------------------------------------|
| 1 2 3
| // 将数据序列化 -> 单行 // 进行数据的网络传输 std::string Json::FastWriter::write(const Value& root);
|
2.3 Reader 类 {#2-3-Reader-类}
|---------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| 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);
|
- VS的配置 {#3-VS的配置} ===================
如果想要在VS中使用编译出的jsoncpp
库,我们还需要做如下配置:
3.1 头文件 {#3-1-头文件}
在编码过程中需要在项目文件中包含从github
下载得到的头文件,有两种处理方式:
-
将头文件放到项目目录下,直接被项目包含引用
-
将头文件放到一个本地固定目录,以后就不再动了,在VS项目属性中设置包含这个目录,我推荐这种
另外,在这个include
目录中还有一个json
子目录,所有的头文件都在这个子目录中,我们不要破坏这个目录结构:
在包含需要的头文件的时候,使用如下这种方式:
|-----------|--------------------------------|
| 1
| #include <json/json.h>
|
把本地的头文件目录在项目属性窗口中进行配置:
我这里头文件是放到了C盘
的jsoncpp目录
中:
3.2 库文件 {#3-2-库文件}
我这里也是将生成的jsoncpp.lib
和jsoncpp.dll
放到了C盘(C:\jsoncpp\lib)
,在VS项目中需要指定这个库路径:
另外,还需要告诉VS需要加载的动态库是哪一个
此处指定的是动态库对应的lib
文件,也就是jsoncpp.lib
配置完成之后,如果项目中使用了jsoncpp
就可以编译通过
了。在程序执行的时候,如果提示找不到jsoncpp
的动态库,把 jsoncpp.dll 拷贝到可执行所在的目录下就可以解决这个问题了。
- 示例代码 {#3-示例代码} =================
比如:我们要将下面这个Json数组写入的一个文件中
|-------------------------|--------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8
| [ 12, 12.34, true, "tom", ["jack", "ace", "robin"], {"sex":"man", "girlfriend":"lucy"} ]
|
|---------------------------|-----------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8 9
| #include <json/json.h> #include <fstream> using namespace Json; int main() { writeJson(); readJson(); }
|
3.1 写json文件 {#3-1-写json文件}
|---------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| 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文件 {#3-2-读json文件}
|---------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| 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文件这种设计思路是坚决不推荐的。