国人自己开发的库, 全部以头文件的形式包含. 在知乎的评测也是各种好评.
其他的解析工具, 玩玩就可以了, 实际项目中, 还是推荐用 RapidJson
.
引子
发现每次都会有一大堆人去说XML和JSON的优缺点, 我觉得也是挺烦的, 下面一句话带过:
1 | <?xml version="1.0" encoding="utf-8" ?> |
再看json:
1 | { |
XML的可读性稍微好一些, 但是冗余信息多, 体积大; 并且解析方便程度来说, JSON完胜.
(但是业界流行程度来说, XML业界广泛认同)
本文主要介绍RapidJson的使用以及一些心得, 如果你对它的源码也感兴趣的话, 可以参考该 链接
(补充, 有人喜欢用 JsonCpp, 不过还是推荐你用RapidJson吧)
(虽然有官方教程, 不过觉得那个教程也是非常啰嗦的)
正文
简介
rapidjson是腾讯的开源json解析框架,用c++实现。由于全部代码仅用header file实现,所以很容易集成到项目中。rapidjson的另一个特点是对json的标准符合程度是100%的(在开启了full precision选项的情况下).
最重要的: RapidJSON is a JSON parser and generator for C++
.
总之, 业界也有好评, 个人觉得比 JsonCpp 好.
安装
RapidJSON 是只有头文件的 C++ 库。只需把 include/rapidjson 目录复制至系统或项目的 include 目录中。
给出我的参考步骤:1
2
3$ git clone https://github.com/miloyip/rapidjson.git
$ cmake .
$ sudo make install
然后, 你的相关库就安装到了 /usr/local/include/
库文件
安装完了, 顺便可以扫一眼, 都有哪些库: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$ tree -L 2 /usr/local/include/rapidjson
/usr/local/include/rapidjson
├── allocators.h
├── document.h
├── encodedstream.h
├── encodings.h
├── error
│ ├── en.h
│ └── error.h
├── filereadstream.h
├── filewritestream.h
├── fwd.h
├── internal
│ ├── biginteger.h
│ ├── diyfp.h
│ ├── dtoa.h
│ ├── ieee754.h
│ ├── itoa.h
│ ├── meta.h
│ ├── pow10.h
│ ├── regex.h
│ ├── stack.h
│ ├── strfunc.h
│ ├── strtod.h
│ └── swap.h
├── istreamwrapper.h
├── memorybuffer.h
├── memorystream.h
├── msinttypes
│ ├── inttypes.h
│ └── stdint.h
├── ostreamwrapper.h
├── pointer.h
├── prettywriter.h
├── rapidjson.h
├── reader.h
├── schema.h
├── stream.h
├── stringbuffer.h
└── writer.h
3 directories, 35 files
案例
先来些简单的案例, 再说相关的API和机制.
此简单例子解析一个 JSON 字符串至一个 document (DOM), 对 DOM 作出简单修改, 最终把 DOM 转换(stringify) 至 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// JSON simple example
// This example does not handle errors.
using namespace rapidjson;
int main() {
// 1. Parse a JSON string into DOM.
const char* json = "{\"project\":\"rapidjson\",\"stars\":10}";
Document d;
d.Parse(json);
// 2. Modify it by DOM.
Value& s = d["stars"];
s.SetInt(s.GetInt() + 1);
// 3. Stringify the DOM
StringBuffer buffer;
Writer<StringBuffer> writer(buffer);
d.Accept(writer);
// Output {"project":"rapidjson","stars":11}
std::cout << buffer.GetString() << std::endl;
return 0;
}
注意此例子并没有处理潜在错误, 比如:1
2
3
4
5// 2. Modify it by DOM.
Value& s = d["stars"];
if( s.IsInt() ) {
s.SetInt(s.GetInt() + 1);
}
编译运行:1
2
3$ g++ -g -Wall -O0 simpledom.cpp -o simpledom -I/usr/local/inlcude/
$ ./simpledom
{"project":"rapidjson","stars":11}
可以简单的得出结论, 解析JSON是围绕 rapidjson::Document
, rapidjson::Value
展开的, 关键性头文件:
- rapidjson/document.h
- rapidjson/writer.h
再来一个实用一点儿的例子:
test.json1
2
3
4
5
6
7
8
9
10
11{
"dictVersion": 1,
"content":
[
{"key": "word1", "value": "单词1"} ,
{"key": "word2", "value": "单词2"} ,
{"key": "word3", "value": "单词3"} ,
{"key": "word4", "value": "单词4"} ,
{"key": "word5", "value": "单词5"}
]
}
对于这种格式化好的文件的读写, 可以采用流式读写处理:
1 | // test.cpp |
编译运行:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15$ g++ -g -Wall test.cpp -o test -I/usr/local/include
$ ./test
v.GetInt() = 1
v["key"].GetString() = word1
v["value"].GetString() = 单词1
v["key"].GetString() = word2
v["value"].GetString() = 单词2
v["key"].GetString() = word3
v["value"].GetString() = 单词3
v["key"].GetString() = word4
v["value"].GetString() = 单词4
v["key"].GetString() = word5
v["value"].GetString() = 单词5
"add a value into array" = add a value into array
buffer.GetString() = {"dictVersion":1,"content":[{"key":"word1","value":"单词1"},{"key":"word2","value":"单词2"},{"key":"word3","value":"单词3"},{"key":"word4","value":"单词4"},{"key":"word5","value":"单词5"},{"key":"word5","value":"单词5"}]}
从上面两个例子, 就可以看出, 只要你给document对象parse(const char*)一个C串, 那么它就会把解析的内容全部存储在document对象里; Value & v = doc[key];
可以拿到单个对象或者Array(这里的array就像一个list或者数组), 结合Value进行读写, Value类含有 HasMember()
, AddMemeber()
之类的方法, 以及和Document类一样重载了 operator[]()
, 而v["key"].GetString()
, v.GetInt()
则可以拿到具体的内容. 上面例子只给出了array如何添加成员, 其实document添加成员, 也是document.AddMember("key buffer", "value buffer-object", document.GetAllocator());
, 并且 document 可以是Object, 也可以是Array, 上面这些例子都是Object.
value[“key”]得到的类型还是Value类型, 当然也可以用v.IsObject()检验:
1 | //Document -> Value |
漂亮的输出到屏幕上(格式修正):1
2
3
4
5
6
StringBuffer sb;
PrettyWriter<StringBuffer> writer(sb);
document.Accept(writer);
puts(sb.GetString());
运行结果大致是: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
35v.GetInt() = 1
v["key"].GetString() = word1
v["value"].GetString() = 单词1
v["key"].GetString() = word2
v["value"].GetString() = 单词2
v["key"].GetString() = word3
v["value"].GetString() = 单词3
v["key"].GetString() = word4
v["value"].GetString() = 单词4
"add a value into array" = add a value into array
buffer.GetString() = {
"dictVersion": 1,
"content": [
{
"key": "word1",
"value": "单词1"
},
{
"key": "word2",
"value": "单词2"
},
{
"key": "word3",
"value": "单词3"
},
{
"key": "word4",
"value": "单词4"
},
{
"key": "word5",
"value": "单词5"
}
]
}
之后如果你想写入文件, 那么就把 sb.GetString()
以文本格式写入文件即可; 或者借助其他的流(下面的例子从stdin和stdout作为流输入和输出):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
using namespace rapidjson;
int main(int, char*[]) {
// Prepare reader and input stream.
Reader reader;
char readBuffer[65536];
/*
ifstream in;
in.open("test.json", ifstream::in);
*/
FileReadStream is(stdin, readBuffer, sizeof(readBuffer));
// Prepare writer and output stream.
char writeBuffer[65536];
FileWriteStream os(stdout, writeBuffer, sizeof(writeBuffer));
PrettyWriter<FileWriteStream> writer(os);
// JSON reader parse from the input stream and let writer generate the output.
if (!reader.Parse<kParseValidateEncodingFlag>(is, writer)) {
fprintf(stderr, "\nError(%u): %s\n",
static_cast<unsigned>(reader.GetErrorOffset()),
GetParseError_En(reader.GetParseErrorCode()));
return 1;
}
return 0;
}
总之, 先把流读到字符串, 之后写解析逻辑.
深入
其实说到这里, 使用上, 基本没有问题了.
进一步的深入, 我给的建议是:
- 先读一下 官方教程.
- 然后把源码下面的 example 下面所有的 demo 全部玩一遍.
教程里面, 拷贝转移以及move()部分, 内存流&文件流部分(包装流效率不高), DOM&SAX部分都是仔细看的, 下面是我的部分笔记:
1 | Value可以用作表示Object, Array, 甚至简单的string |
这个时候(把那个几个example玩熟), 对这个库已经很熟悉了.
再深入, 就结合他的官网以及源码慢慢琢磨吧.
尾巴
没太多可说的, 写多了就熟悉了.