]>
Commit | Line | Data |
---|---|---|
31f18b77 FG |
1 | # 常见问题 |
2 | ||
3 | [TOC] | |
4 | ||
5 | ## 一般问题 | |
6 | ||
7 | 1. RapidJSON 是什么? | |
8 | ||
9 | RapidJSON 是一个 C++ 库,用于解析及生成 JSON。读者可参考它的所有 [特点](doc/features.zh-cn.md)。 | |
10 | ||
11 | 2. 为什么称作 RapidJSON? | |
12 | ||
13 | 它的灵感来自于 [RapidXML](http://rapidxml.sourceforge.net/),RapidXML 是一个高速的 XML DOM 解析器。 | |
14 | ||
15 | 3. RapidJSON 与 RapidXML 相似么? | |
16 | ||
17 | RapidJSON 借镜了 RapidXML 的一些设计, 包括原位(*in situ*)解析、只有头文件的库。但两者的 API 是完全不同的。此外 RapidJSON 也提供许多 RapidXML 没有的特点。 | |
18 | ||
19 | 4. RapidJSON 是免费的么? | |
20 | ||
21 | 是的,它在 MIT 特許條款下免费。它可用于商业软件。详情请参看 [license.txt](https://github.com/miloyip/rapidjson/blob/master/license.txt)。 | |
22 | ||
23 | 5. RapidJSON 很小么?它有何依赖? | |
24 | ||
25 | 是的。在 Windows 上,一个解析 JSON 并打印出统计的可执行文件少于 30KB。 | |
26 | ||
27 | RapidJSON 仅依赖于 C++ 标准库。 | |
28 | ||
29 | 6. 怎样安装 RapidJSON? | |
30 | ||
31 | 见 [安装一节](../readme.zh-cn.md#安装)。 | |
32 | ||
33 | 7. RapidJSON 能否运行于我的平台? | |
34 | ||
35 | 社区已在多个操作系统/编译器/CPU 架构的组合上测试 RapidJSON。但我们无法确保它能运行于你特定的平台上。只需要生成及执行单元测试便能获取答案。 | |
36 | ||
37 | 8. RapidJSON 支持 C++03 么?C++11 呢? | |
38 | ||
39 | RapidJSON 开始时在 C++03 上实现。后来加入了可选的 C++11 特性支持(如转移构造函数、`noexcept`)。RapidJSON 应该兼容所有遵从 C++03 或 C++11 的编译器。 | |
40 | ||
41 | 9. RapidJSON 是否真的用于实际应用? | |
42 | ||
43 | 是的。它被配置于前台及后台的真实应用中。一个社区成员说 RapidJSON 在他们的系统中每日解析 5 千万个 JSON。 | |
44 | ||
45 | 10. RapidJSON 是如何被测试的? | |
46 | ||
47 | RapidJSON 包含一组单元测试去执行自动测试。[Travis](https://travis-ci.org/miloyip/rapidjson/)(供 Linux 平台)及 [AppVeyor](https://ci.appveyor.com/project/miloyip/rapidjson/)(供 Windows 平台)会对所有修改进行编译及执行单元测试。在 Linux 下还会使用 Valgrind 去检测内存泄漏。 | |
48 | ||
49 | 11. RapidJSON 是否有完整的文档? | |
50 | ||
51 | RapidJSON 提供了使用手册及 API 说明文档。 | |
52 | ||
53 | 12. 有没有其他替代品? | |
54 | ||
55 | 有许多替代品。例如 [nativejson-benchmark](https://github.com/miloyip/nativejson-benchmark) 列出了一些开源的 C/C++ JSON 库。[json.org](http://www.json.org/) 也有一个列表。 | |
56 | ||
57 | ## JSON | |
58 | ||
59 | 1. 什么是 JSON? | |
60 | ||
61 | JSON (JavaScript Object Notation) 是一个轻量的数据交换格式。它使用人类可读的文本格式。更多关于 JSON 的细节可考 [RFC7159](http://www.ietf.org/rfc/rfc7159.txt) 及 [ECMA-404](http://www.ecma-international.org/publications/standards/Ecma-404.htm)。 | |
62 | ||
63 | 2. JSON 有什么应用场合? | |
64 | ||
65 | JSON 常用于网页应用程序,以传送结构化数据。它也可作为文件格式用于数据持久化。 | |
66 | ||
67 | 2. RapidJSON 是否符合 JSON 标准? | |
68 | ||
69 | 是。RapidJSON 完全符合 [RFC7159](http://www.ietf.org/rfc/rfc7159.txt) 及 [ECMA-404](http://www.ecma-international.org/publications/standards/Ecma-404.htm)。它能处理一些特殊情况,例如支持 JSON 字符串中含有空字符及代理对(surrogate pair)。 | |
70 | ||
71 | 3. RapidJSON 是否支持宽松的语法? | |
72 | ||
73 | 现时不支持。RapidJSON 只支持严格的标准格式。宽松语法现时在这 [issue](https://github.com/miloyip/rapidjson/issues/36) 中进行讨论。 | |
74 | ||
75 | ## DOM 与 SAX | |
76 | ||
77 | 1. 什么是 DOM 风格 API? | |
78 | ||
79 | Document Object Model(DOM)是一个储存于内存的 JSON 表示方式,用于查询及修改 JSON。 | |
80 | ||
81 | 2. 什么是 SAX 风格 API? | |
82 | ||
83 | SAX 是一个事件驱动的 API,用于解析及生成 JSON。 | |
84 | ||
85 | 3. 我应用 DOM 还是 SAX? | |
86 | ||
87 | DOM 易于查询及修改。SAX 则是非常快及省内存的,但通常较难使用。 | |
88 | ||
89 | 4. 什么是原位(*in situ*)解析? | |
90 | ||
91 | 原位解析会把 JSON 字符串直接解码至输入的 JSON 中。这是一个优化,可减少内存消耗及提升性能,但输入的 JSON 会被更改。进一步细节请参考 [原位解析](doc/dom.zh-cn.md) 。 | |
92 | ||
93 | 5. 什么时候会产生解析错误? | |
94 | ||
95 | 当输入的 JSON 包含非法语法,或不能表示一个值(如 Number 太大),或解析器的处理器中断解析过程,解析器都会产生一个错误。详情请参考 [解析错误](doc/dom.zh-cn.md)。 | |
96 | ||
97 | 6. 有什么错误信息? | |
98 | ||
99 | 错误信息存储在 `ParseResult`,它包含错误代号及偏移值(从 JSON 开始至错误处的字符数目)。可以把错误代号翻译为人类可读的错误讯息。 | |
100 | ||
101 | 7. 为何不只使用 `double` 去表示 JSON number? | |
102 | ||
103 | 一些应用需要使用 64 位无号/有号整数。这些整数不能无损地转换成 `double`。因此解析器会检测一个 JSON number 是否能转换至各种整数类型及 `double`。 | |
104 | ||
105 | 8. 如何清空并最小化 `document` 或 `value` 的容量? | |
106 | ||
107 | 调用 `SetXXX()` 方法 - 这些方法会调用析构函数,并重建空的 Object 或 Array: | |
108 | ||
109 | ~~~~~~~~~~cpp | |
110 | Document d; | |
111 | ... | |
112 | d.SetObject(); // clear and minimize | |
113 | ~~~~~~~~~~ | |
114 | ||
115 | 另外,也可以参考在 [C++ swap with temporary idiom](https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Clear-and-minimize) 中的一种等价的方法: | |
116 | ~~~~~~~~~~cpp | |
117 | Value(kObjectType).Swap(d); | |
118 | ~~~~~~~~~~ | |
119 | 或者,使用这个稍微长一点的代码也能完成同样的事情: | |
120 | ~~~~~~~~~~cpp | |
121 | d.Swap(Value(kObjectType).Move()); | |
122 | ~~~~~~~~~~ | |
123 | ||
124 | 9. 如何将一个 `document` 节点插入到另一个 `document` 中? | |
125 | ||
126 | 比如有以下两个 document(DOM): | |
127 | ~~~~~~~~~~cpp | |
128 | Document person; | |
129 | person.Parse("{\"person\":{\"name\":{\"first\":\"Adam\",\"last\":\"Thomas\"}}}"); | |
130 | ||
131 | Document address; | |
132 | address.Parse("{\"address\":{\"city\":\"Moscow\",\"street\":\"Quiet\"}}"); | |
133 | ~~~~~~~~~~ | |
134 | 假设我们希望将整个 `address` 插入到 `person` 中,作为其的一个子节点: | |
135 | ~~~~~~~~~~js | |
136 | { "person": { | |
137 | "name": { "first": "Adam", "last": "Thomas" }, | |
138 | "address": { "city": "Moscow", "street": "Quiet" } | |
139 | } | |
140 | } | |
141 | ~~~~~~~~~~ | |
142 | ||
143 | 在插入节点的过程中需要注意 `document` 和 `value` 的生命周期并且正确地使用 allocator 进行内存分配和管理。 | |
144 | ||
145 | 一个简单有效的方法就是修改上述 `address` 变量的定义,让其使用 `person` 的 allocator 初始化,然后将其添加到根节点。 | |
146 | ||
147 | ~~~~~~~~~~cpp | |
148 | Documnet address(person.GetAllocator()); | |
149 | ... | |
150 | person["person"].AddMember("address", address["address"], person.GetAllocator()); | |
151 | ~~~~~~~~~~ | |
152 | 当然,如果你不想通过显式地写出 `address` 的 key 来得到其值,可以使用迭代器来实现: | |
153 | ~~~~~~~~~~cpp | |
154 | auto addressRoot = address.MemberBegin(); | |
155 | person["person"].AddMember(addressRoot->name, addressRoot->value, person.GetAllocator()); | |
156 | ~~~~~~~~~~ | |
157 | ||
158 | 此外,还可以通过深拷贝 address document 来实现: | |
159 | ~~~~~~~~~~cpp | |
160 | Value addressValue = Value(address["address"], person.GetAllocator()); | |
161 | person["person"].AddMember("address", addressValue, person.GetAllocator()); | |
162 | ~~~~~~~~~~ | |
163 | ||
164 | ## Document/Value (DOM) | |
165 | ||
166 | 1. 什么是转移语意?为什么? | |
167 | ||
168 | `Value` 不用复制语意,而使用了转移语意。这是指,当把来源值赋值于目标值时,来源值的所有权会转移至目标值。 | |
169 | ||
170 | 由于转移快于复制,此设计决定强迫使用者注意到复制的消耗。 | |
171 | ||
172 | 2. 怎样去复制一个值? | |
173 | ||
174 | 有两个 API 可用:含 allocator 的构造函数,以及 `CopyFrom()`。可参考 [深复制 Value](doc/tutorial.zh-cn.md) 里的用例。 | |
175 | ||
176 | 3. 为什么我需要提供字符串的长度? | |
177 | ||
178 | 由于 C 字符串是空字符结尾的,需要使用 `strlen()` 去计算其长度,这是线性复杂度的操作。若使用者已知字符串的长度,对很多操作来说会造成不必要的消耗。 | |
179 | ||
180 | 此外,RapidJSON 可处理含有 `\u0000`(空字符)的字符串。若一个字符串含有空字符,`strlen()` 便不能返回真正的字符串长度。在这种情况下使用者必须明确地提供字符串长度。 | |
181 | ||
182 | 4. 为什么在许多 DOM 操作 API 中要提供分配器作为参数? | |
183 | ||
184 | 由于这些 API 是 `Value` 的成员函数,我们不希望为每个 `Value` 储存一个分配器指针。 | |
185 | ||
186 | 5. 它会转换各种数值类型么? | |
187 | ||
188 | 当使用 `GetInt()`、`GetUint()` 等 API 时,可能会发生转换。对于整数至整数转换,仅当保证转换安全才会转换(否则会断言失败)。然而,当把一个 64 位有号/无号整数转换至 double 时,它会转换,但有可能会损失精度。含有小数的数字、或大于 64 位的整数,都只能使用 `GetDouble()` 获取其值。 | |
189 | ||
190 | ## Reader/Writer (SAX) | |
191 | ||
192 | 1. 为什么不仅仅用 `printf` 输出一个 JSON?为什么需要 `Writer`? | |
193 | ||
194 | 最重要的是,`Writer` 能确保输出的 JSON 是格式正确的。错误地调用 SAX 事件(如 `StartObject()` 错配 `EndArray()`)会造成断言失败。此外,`Writer` 会把字符串进行转义(如 `\n`)。最后,`printf()` 的数值输出可能并不是一个合法的 JSON number,特别是某些 locale 会有数字分隔符。而且 `Writer` 的数值字符串转换是使用非常快的算法来实现的,胜过 `printf()` 及 `iostream`。 | |
195 | ||
196 | 2. 我能否暂停解析过程,并在稍后继续? | |
197 | ||
198 | 基于性能考虑,目前版本并不直接支持此功能。然而,若执行环境支持多线程,使用者可以在另一线程解析 JSON,并通过阻塞输入流去暂停。 | |
199 | ||
200 | ## Unicode | |
201 | ||
202 | 1. 它是否支持 UTF-8、UTF-16 及其他格式? | |
203 | ||
204 | 是。它完全支持 UTF-8、UTF-16(大端/小端)、UTF-32(大端/小端)及 ASCII。 | |
205 | ||
206 | 2. 它能否检测编码的合法性? | |
207 | ||
208 | 能。只需把 `kParseValidateEncodingFlag` 参考传给 `Parse()`。若发现在输入流中有非法的编码,它就会产生 `kParseErrorStringInvalidEncoding` 错误。 | |
209 | ||
210 | 3. 什么是代理对(surrogate pair)?RapidJSON 是否支持? | |
211 | ||
212 | JSON 使用 UTF-16 编码去转义 Unicode 字符,例如 `\u5927` 表示中文字“大”。要处理基本多文种平面(basic multilingual plane,BMP)以外的字符时,UTF-16 会把那些字符编码成两个 16 位值,这称为 UTF-16 代理对。例如,绘文字字符 U+1F602 在 JSON 中可被编码成 `\uD83D\uDE02`。 | |
213 | ||
214 | RapidJSON 完全支持解析及生成 UTF-16 代理对。 | |
215 | ||
216 | 4. 它能否处理 JSON 字符串中的 `\u0000`(空字符)? | |
217 | ||
218 | 能。RapidJSON 完全支持 JSON 字符串中的空字符。然而,使用者需要注意到这件事,并使用 `GetStringLength()` 及相关 API 去取得字符串真正长度。 | |
219 | ||
220 | 5. 能否对所有非 ASCII 字符输出成 `\uxxxx` 形式? | |
221 | ||
222 | 可以。只要在 `Writer` 中使用 `ASCII<>` 作为输出编码参数,就可以强逼转义那些字符。 | |
223 | ||
224 | ## 流 | |
225 | ||
226 | 1. 我有一个很大的 JSON 文件。我应否把它整个载入内存中? | |
227 | ||
228 | 使用者可使用 `FileReadStream` 去逐块读入文件。但若使用于原位解析,必须载入整个文件。 | |
229 | ||
230 | 2. 我能否解析一个从网络上串流进来的 JSON? | |
231 | ||
232 | 可以。使用者可根据 `FileReadStream` 的实现,去实现一个自定义的流。 | |
233 | ||
234 | 3. 我不知道一些 JSON 将会使用哪种编码。怎样处理它们? | |
235 | ||
236 | 你可以使用 `AutoUTFInputStream`,它能自动检测输入流的编码。然而,它会带来一些性能开销。 | |
237 | ||
238 | 4. 什么是 BOM?RapidJSON 怎样处理它? | |
239 | ||
240 | [字节顺序标记(byte order mark, BOM)](http://en.wikipedia.org/wiki/Byte_order_mark) 有时会出现于文件/流的开始,以表示其 UTF 编码类型。 | |
241 | ||
242 | RapidJSON 的 `EncodedInputStream` 可检测/跳过 BOM。`EncodedOutputStream` 可选择是否写入 BOM。可参考 [编码流](doc/stream.zh-cn.md) 中的例子。 | |
243 | ||
244 | 5. 为什么会涉及大端/小端? | |
245 | ||
246 | 流的大端/小端是 UTF-16 及 UTF-32 流要处理的问题,而 UTF-8 不需要处理。 | |
247 | ||
248 | ## 性能 | |
249 | ||
250 | 1. RapidJSON 是否真的快? | |
251 | ||
252 | 是。它可能是最快的开源 JSON 库。有一个 [评测](https://github.com/miloyip/nativejson-benchmark) 评估 C/C++ JSON 库的性能。 | |
253 | ||
254 | 2. 为什么它会快? | |
255 | ||
256 | RapidJSON 的许多设计是针对时间/空间性能来设计的,这些决定可能会影响 API 的易用性。此外,它也使用了许多底层优化(内部函数/intrinsic、SIMD)及特别的算法(自定义的 double 至字符串转换、字符串至 double 的转换)。 | |
257 | ||
258 | 3. 什是是 SIMD?它如何用于 RapidJSON? | |
259 | ||
260 | [SIMD](http://en.wikipedia.org/wiki/SIMD) 指令可以在现代 CPU 中执行并行运算。RapidJSON 支持了 Intel 的 SSE2/SSE4.2 去加速跳过空白字符。在解析含缩进的 JSON 时,这能提升性能。只要定义名为 `RAPIDJSON_SSE2` 或 `RAPIDJSON_SSE42` 的宏,就能启动这个功能。然而,若在不支持这些指令集的机器上执行这些可执行文件,会导致崩溃。 | |
261 | ||
262 | 4. 它会消耗许多内存么? | |
263 | ||
264 | RapidJSON 的设计目标是减低内存占用。 | |
265 | ||
266 | 在 SAX API 中,`Reader` 消耗的内存与 JSON 树深度加上最长 JSON 字符成正比。 | |
267 | ||
268 | 在 DOM API 中,每个 `Value` 在 32/64 位架构下分别消耗 16/24 字节。RapidJSON 也使用一个特殊的内存分配器去减少分配的额外开销。 | |
269 | ||
270 | 5. 高性能的意义何在? | |
271 | ||
272 | 有些应用程序需要处理非常大的 JSON 文件。而有些后台应用程序需要处理大量的 JSON。达到高性能同时改善延时及吞吐量。更广义来说,这也可以节省能源。 | |
273 | ||
274 | ## 八挂 | |
275 | ||
276 | 1. 谁是 RapidJSON 的开发者? | |
277 | ||
278 | 叶劲峰(Milo Yip,[miloyip](https://github.com/miloyip))是 RapidJSON 的原作者。全世界许多贡献者一直在改善 RapidJSON。Philipp A. Hartmann([pah](https://github.com/pah))实现了许多改进,也设置了自动化测试,而且还参与许多社区讨论。丁欧南(Don Ding,[thebusytypist](https://github.com/thebusytypist))实现了迭代式解析器。Andrii Senkovych([jollyroger](https://github.com/jollyroger))完成了向 CMake 的迁移。Kosta([Kosta-Github](https://github.com/Kosta-Github))提供了一个非常灵巧的短字符串优化。也需要感谢其他献者及社区成员。 | |
279 | ||
280 | 2. 为何你要开发 RapidJSON? | |
281 | ||
282 | 在 2011 年开始这项目是,它仅一个兴趣项目。Milo Yip 是一个游戏程序员,他在那时候认识到 JSON 并希望在未来的项目中使用。由于 JSON 好像很简单,他希望写一个仅有头文件并且快速的程序库。 | |
283 | ||
284 | 3. 为什么开发中段有一段长期空档? | |
285 | ||
286 | 主要是个人因素,例如加入新家庭成员。另外,Milo Yip 也花了许多业馀时间去翻译 Jason Gregory 的《Game Engine Architecture》至中文版《游戏引擎架构》。 | |
287 | ||
288 | 4. 为什么这个项目从 Google Code 搬到 GitHub? | |
289 | ||
290 | 这是大势所趋,而且 GitHub 更为强大及方便。 |