C++Jsonjsoncpp的编译和使用
苏丙榅在前面的文章中为大家介绍了C语言中如何使用cjson处理json数据,接下来讲一下json
在C++中的处理,这里给大家介绍一个开源的库jsoncpp
。
1. 下载和编译
1.1 下载
下载 jsoncpp
Jsoncpp是个跨平台的C++开源库,提供的类为我们提供了很便捷的操作,而且使用的人也很多。在使用之前我们首先要从github
仓库下载源码,地址如下:
1
| https://github.com/open-source-parsers/jsoncpp
|
下载 cmake 工具
由于C++程序猿都是基于VS进行项目开发,下载的源码我们一般不会直接使用,而且将其编译成相应的库文件(动态库或者静态库),这样不论是从使用或者部署的角度来说,操作起来都会更方便一些。
但是,从github
下载的源码不能直接通过VS打开
,编译就更谈不上了。它提供的默认编译方式是cmake
。我们可以通过使用cmake
工具将下载的jsoncpp
源码生成一个VS项目
,这样就可以通过VS
编译出需要的库文件了。
CMake工具的官方下载地址如下:
1
| https://cmake.org/download/
|
在这最新的安装包,根据向导完成安装即可。
1.2 生成VS项目
打开安装好的CMake
工具
需要在工具中指定本地的jsoncpp
路径(git clone 之后就会得到这个目录),这是我本地的目录:
第二个需要指定的是一个文件存储路径(生成的VS项目会保存到这个目录下),保证这是一个本地的有效目录即可。
设置好之后进行配置,点击Configure
按钮
此处需要设置一下,VS的版本以及生成器的平台位数,不填默认就是64位。
配置完成,开始生成VS项目。
打开在CMake
工具中指定的生成目录,我这里是D:\output-project
,基于项目文件jsoncpp.sln
打开这个VS项目。
1.3 编译
基于生成的项目文件打开VS项目之后,可以看到里边有很多子项目
我们只需要编译上图标记的那一个就可以了,编译成功之后就可以得到我们需要的库文件了。
通过输出的日志信息,就能找到我们想要的动态库了,把这两个文件收集起来备用。
2. jsoncpp 的使用
jsoncpp
库中的类被定义到了一个Json
命名空间中,建议在使用这个库的时候先声明这个命名空间:
使用jsoncpp
库解析json
格式的数据,我们只需要掌握三个类:
Value 类
:将json支持的数据类型进行了包装,最终得到一个Value类型
FastWriter类
:将Value对象中的数据序列化为字符串
Reader类
:反序列化, 将json字符串 解析成 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
| 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对象转换为实际类型
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数组的操作
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;
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对象的操作
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);
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
1 2 3
|
std::string toStyledString() const;
|
2.2 FastWriter 类
1 2 3
|
std::string Json::FastWriter::write(const Value& root);
|
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字符串中的注释信息
bool Json::Reader::parse(const char* beginDoc, const char* endDoc, Value& root, bool collectComments = true);
bool Json::Reader::parse(std::istream& is, Value& root, bool collectComments = true);
|
3. VS的配置
如果想要在VS中使用编译出的jsoncpp
库,我们还需要做如下配置:
3.1 头文件
在编码过程中需要在项目文件中包含从github
下载得到的头文件,有两种处理方式:
将头文件放到项目目录下,直接被项目包含引用
将头文件放到一个本地固定目录,以后就不再动了,在VS项目属性中设置包含这个目录,我推荐这种
另外,在这个include
目录中还有一个json
子目录,所有的头文件都在这个子目录中,我们不要破坏这个目录结构:
在包含需要的头文件的时候,使用如下这种方式:
把本地的头文件目录在项目属性窗口中进行配置:
我这里头文件是放到了C盘
的jsoncpp目录
中:
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文件
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 root; 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文件
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() { ifstream ifs("test.json"); Value root; Reader r; r.parse(ifs, root); if (root.isArray()) { for (int i = 0; i < root.size(); ++i) { Value item = root[i]; 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()) { 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文件这种设计思路是坚决不推荐的。