Welcome to mirror list, hosted at ThFree Co, Russian Federation.

md_doc_tutorial_8zh-cn.html « zh-cn - github.com/miloyip/rapidjson.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 073025bdffebded0797f6f85d296b90910151bb0 (plain)
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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
<!-- HTML header for doxygen 1.8.7-->
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=9"/>
<meta name="generator" content="Doxygen 1.8.7"/>
<title>RapidJSON: 教程</title>
<link href="tabs.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="dynsections.js"></script>
<link href="navtree.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="resize.js"></script>
<script type="text/javascript" src="navtree.js"></script>
<script type="text/javascript">
  $(document).ready(initResizable);
  $(window).load(resizeHeight);
</script>
<link href="search/search.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="search/search.js"></script>
<script type="text/javascript">
  $(document).ready(function() { searchBox.OnSelectItem(0); });
</script>
<link href="doxygen.css" rel="stylesheet" type="text/css" />
<link href="doxygenextra.css" rel="stylesheet" type="text/css"/>
</head>
<script>
  (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
  (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
  m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
  })(window,document,'script','//www.google-analytics.com/analytics.js','ga');
  ga('create', 'UA-63929386-1', 'auto');
  ga('send', 'pageview');
</script>
<body>
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
<div id="topbanner"><a href="https://github.com/miloyip/rapidjson" title="RapidJSON GitHub"><i class="githublogo"></i></a></div>
        <div id="MSearchBox" class="MSearchBoxInactive">
        <span class="left">
          <img id="MSearchSelect" src="search/mag_sel.png"
               onmouseover="return searchBox.OnSearchSelectShow()"
               onmouseout="return searchBox.OnSearchSelectHide()"
               alt=""/>
          <input type="text" id="MSearchField" value="搜索" accesskey="S"
               onfocus="searchBox.OnSearchFieldFocus(true)" 
               onblur="searchBox.OnSearchFieldFocus(false)" 
               onkeyup="searchBox.OnSearchFieldChange(event)"/>
          </span><span class="right">
            <a id="MSearchClose" href="javascript:searchBox.CloseResultsWindow()"><img id="MSearchCloseImg" border="0" src="search/close.png" alt=""/></a>
          </span>
        </div>
<!-- end header part -->
<!-- 制作者 Doxygen 1.8.7 -->
<script type="text/javascript">
var searchBox = new SearchBox("searchBox", "search",false,'搜索');
</script>
</div><!-- top -->
<div id="side-nav" class="ui-resizable side-nav-resizable">
  <div id="nav-tree">
    <div id="nav-tree-contents">
      <div id="nav-sync" class="sync"></div>
    </div>
  </div>
  <div id="splitbar" style="-moz-user-select:none;" 
       class="ui-resizable-handle">
  </div>
</div>
<script type="text/javascript">
$(document).ready(function(){initNavTree('md_doc_tutorial_8zh-cn.html','');});
</script>
<div id="doc-content">
<!-- window showing the filter options -->
<div id="MSearchSelectWindow"
     onmouseover="return searchBox.OnSearchSelectShow()"
     onmouseout="return searchBox.OnSearchSelectHide()"
     onkeydown="return searchBox.OnSearchSelectKey(event)">
<a class="SelectItem" href="javascript:void(0)" onclick="searchBox.OnSelectItem(0)"><span class="SelectionMark">&#160;</span>全部</a><a class="SelectItem" href="javascript:void(0)" onclick="searchBox.OnSelectItem(1)"><span class="SelectionMark">&#160;</span>类</a><a class="SelectItem" href="javascript:void(0)" onclick="searchBox.OnSelectItem(2)"><span class="SelectionMark">&#160;</span>命名空间</a><a class="SelectItem" href="javascript:void(0)" onclick="searchBox.OnSelectItem(3)"><span class="SelectionMark">&#160;</span>文件</a><a class="SelectItem" href="javascript:void(0)" onclick="searchBox.OnSelectItem(4)"><span class="SelectionMark">&#160;</span>函数</a><a class="SelectItem" href="javascript:void(0)" onclick="searchBox.OnSelectItem(5)"><span class="SelectionMark">&#160;</span>变量</a><a class="SelectItem" href="javascript:void(0)" onclick="searchBox.OnSelectItem(6)"><span class="SelectionMark">&#160;</span>类型定义</a><a class="SelectItem" href="javascript:void(0)" onclick="searchBox.OnSelectItem(7)"><span class="SelectionMark">&#160;</span>枚举</a><a class="SelectItem" href="javascript:void(0)" onclick="searchBox.OnSelectItem(8)"><span class="SelectionMark">&#160;</span>枚举值</a><a class="SelectItem" href="javascript:void(0)" onclick="searchBox.OnSelectItem(9)"><span class="SelectionMark">&#160;</span>友元</a><a class="SelectItem" href="javascript:void(0)" onclick="searchBox.OnSelectItem(10)"><span class="SelectionMark">&#160;</span>宏定义</a><a class="SelectItem" href="javascript:void(0)" onclick="searchBox.OnSelectItem(11)"><span class="SelectionMark">&#160;</span>组</a><a class="SelectItem" href="javascript:void(0)" onclick="searchBox.OnSelectItem(12)"><span class="SelectionMark">&#160;</span>页</a></div>

<!-- iframe showing the search results (closed by default) -->
<div id="MSearchResultsWindow">
<iframe src="javascript:void(0)" frameborder="0" 
        name="MSearchResults" id="MSearchResults">
</iframe>
</div>

<div class="header">
  <div class="headertitle">
<div class="title">教程 </div>  </div>
</div><!--header-->
<div class="contents">
<div class="toc"><h3>目录</h3>
<ul><li class="level1"><a href="#ValueDocument">Value 及 Document</a></li>
<li class="level1"><a href="#QueryValue">查询Value</a><ul><li class="level2"><a href="#QueryArray">查询Array</a></li>
<li class="level2"><a href="#QueryObject">查询Object</a></li>
<li class="level2"><a href="#QueryNumber">查询Number</a></li>
<li class="level2"><a href="#QueryString">查询String</a></li>
</ul>
</li>
<li class="level1"><a href="#CreateModifyValues">创建/修改值</a><ul><li class="level2"><a href="#ChangeValueType">改变Value类型</a></li>
<li class="level2"><a href="#MoveSemantics">转移语意(Move Semantics)</a><ul><li class="level3"><a href="#TemporaryValues">转移语意及临时值</a></li>
</ul>
</li>
<li class="level2"><a href="#CreateString">创建String</a></li>
<li class="level2"><a href="#ModifyArray">修改Array</a></li>
<li class="level2"><a href="#ModifyObject">修改Object</a></li>
<li class="level2"><a href="#DeepCopyValue">深复制Value</a></li>
<li class="level2"><a href="#SwapValues">交换Value</a></li>
</ul>
</li>
<li class="level1"><a href="#WhatsNext">下一部分</a></li>
</ul>
</div>
<div class="textblock"><p>本教程简介文件对象模型(Document Object Model, DOM)API。</p>
<p>如<a href="../readme.zh-cn.md#用法一览">用法一览</a>中所示,可以解析一个JSON至DOM,然后就可以轻松查询及修改DOM,并最终转换回JSON。</p>
<h1><a class="anchor" id="ValueDocument"></a>
Value 及 Document</h1>
<p>每个JSON值都储存为<code>Value</code>类,而<code>Document</code>类则表示整个DOM,它存储了一个DOM树的根<code>Value</code>。RapidJSON的所有公开类型及函数都在<code>rapidjson</code>命名空间中。</p>
<h1><a class="anchor" id="QueryValue"></a>
查询Value</h1>
<p>在本节中,我们会使用到<code>example/tutorial/tutorial.cpp</code>中的代码片段。</p>
<p>假设我们用C语言的字符串储存一个JSON(<code>const char* json</code>): </p><div class="fragment"><div class="line">{</div>
<div class="line">    <span class="stringliteral">&quot;hello&quot;</span>: <span class="stringliteral">&quot;world&quot;</span>,</div>
<div class="line">    <span class="stringliteral">&quot;t&quot;</span>: true ,</div>
<div class="line">    <span class="stringliteral">&quot;f&quot;</span>: <span class="keyword">false</span>,</div>
<div class="line">    <span class="stringliteral">&quot;n&quot;</span>: null,</div>
<div class="line">    <span class="stringliteral">&quot;i&quot;</span>: 123,</div>
<div class="line">    <span class="stringliteral">&quot;pi&quot;</span>: 3.1416,</div>
<div class="line">    <span class="stringliteral">&quot;a&quot;</span>: [1, 2, 3, 4]</div>
<div class="line">}</div>
</div><!-- fragment --><p>把它解析至一个<code>Document</code>: </p><div class="fragment"><div class="line"><span class="preprocessor">#include &quot;<a class="code" href="document_8h.html">rapidjson/document.h</a>&quot;</span></div>
<div class="line"></div>
<div class="line"><span class="keyword">using namespace </span>rapidjson;</div>
<div class="line"></div>
<div class="line"><span class="comment">// ...</span></div>
<div class="line"><a class="code" href="classrapidjson_1_1_generic_document.html">Document</a> document;</div>
<div class="line">document.<a class="code" href="classrapidjson_1_1_generic_document.html#aea842b533a858c9a3861451ad9e8642c">Parse</a>(json);</div>
</div><!-- fragment --><p>那么现在该JSON就会被解析至<code>document</code>中,成为一棵*DOM树*:</p>
<div class="image">
<img src="tutorial.png" alt="tutorial.png"/>
<div class="caption">
教程中的DOM</div></div>
<p> 自从RFC 7159作出更新,合法JSON文件的根可以是任何类型的JSON值。而在较早的RFC 4627中,根值只允许是Object或Array。而在上述例子中,根是一个Object。 </p><div class="fragment"><div class="line">assert(document.IsObject());</div>
</div><!-- fragment --><p>让我们查询一下根Object中有没有<code>"hello"</code>成员。由于一个<code>Value</code>可包含不同类型的值,我们可能需要验证它的类型,并使用合适的API去获取其值。在此例中,<code>"hello"</code>成员关联到一个JSON String。 </p><div class="fragment"><div class="line">assert(document.HasMember(<span class="stringliteral">&quot;hello&quot;</span>));</div>
<div class="line">assert(document[<span class="stringliteral">&quot;hello&quot;</span>].IsString());</div>
<div class="line">printf(<span class="stringliteral">&quot;hello = %s\n&quot;</span>, document[<span class="stringliteral">&quot;hello&quot;</span>].GetString());</div>
</div><!-- fragment --><div class="fragment"><div class="line">world</div>
</div><!-- fragment --><p>JSON True/False值是以<code>bool</code>表示的。 </p><div class="fragment"><div class="line">assert(document[<span class="stringliteral">&quot;t&quot;</span>].IsBool());</div>
<div class="line">printf(<span class="stringliteral">&quot;t = %s\n&quot;</span>, document[<span class="stringliteral">&quot;t&quot;</span>].GetBool() ? <span class="stringliteral">&quot;true&quot;</span> : <span class="stringliteral">&quot;false&quot;</span>);</div>
</div><!-- fragment --><div class="fragment"><div class="line"><span class="keyword">true</span></div>
</div><!-- fragment --><p>JSON Null值可用<code>IsNull()</code>查询。 </p><div class="fragment"><div class="line">printf(<span class="stringliteral">&quot;n = %s\n&quot;</span>, document[<span class="stringliteral">&quot;n&quot;</span>].IsNull() ? <span class="stringliteral">&quot;null&quot;</span> : <span class="stringliteral">&quot;?&quot;</span>);</div>
</div><!-- fragment --><div class="fragment"><div class="line">null</div>
</div><!-- fragment --><p>JSON Number类型表示所有数值。然而,C++需要使用更专门的类型。</p>
<div class="fragment"><div class="line">assert(document[<span class="stringliteral">&quot;i&quot;</span>].IsNumber());</div>
<div class="line"></div>
<div class="line"><span class="comment">// 在此情况下,IsUint()/IsInt64()/IsUInt64()也会返回 true</span></div>
<div class="line">assert(document[<span class="stringliteral">&quot;i&quot;</span>].IsInt());          </div>
<div class="line">printf(<span class="stringliteral">&quot;i = %d\n&quot;</span>, document[<span class="stringliteral">&quot;i&quot;</span>].GetInt());</div>
<div class="line"><span class="comment">// 另一种用法: (int)document[&quot;i&quot;]</span></div>
<div class="line"></div>
<div class="line">assert(document[<span class="stringliteral">&quot;pi&quot;</span>].IsNumber());</div>
<div class="line">assert(document[<span class="stringliteral">&quot;pi&quot;</span>].IsDouble());</div>
<div class="line">printf(<span class="stringliteral">&quot;pi = %g\n&quot;</span>, document[<span class="stringliteral">&quot;pi&quot;</span>].GetDouble());</div>
</div><!-- fragment --><div class="fragment"><div class="line">i = 123</div>
<div class="line">pi = 3.1416</div>
</div><!-- fragment --><p>JSON Array包含一些元素。 </p><div class="fragment"><div class="line"><span class="comment">// 使用引用来连续访问,方便之余还更高效。</span></div>
<div class="line"><span class="keyword">const</span> <a class="code" href="namespacerapidjson.html#aa65fc9fb381b2cbc54f98673eadd6505">Value</a>&amp; a = document[<span class="stringliteral">&quot;a&quot;</span>];</div>
<div class="line">assert(a.IsArray());</div>
<div class="line"><span class="keywordflow">for</span> (<a class="code" href="namespacerapidjson.html#a44eb33eaa523e36d466b1ced64b85c84">SizeType</a> i = 0; i &lt; a.Size(); i++) <span class="comment">// 使用 SizeType 而不是 size_t</span></div>
<div class="line">        printf(<span class="stringliteral">&quot;a[%d] = %d\n&quot;</span>, i, a[i].GetInt());</div>
</div><!-- fragment --><div class="fragment"><div class="line">a[0] = 1</div>
<div class="line">a[1] = 2</div>
<div class="line">a[2] = 3</div>
<div class="line">a[3] = 4</div>
</div><!-- fragment --><p>注意,RapidJSON并不自动转换各种JSON类型。例如,对一个String的Value调用<code>GetInt()</code>是非法的。在调试模式下,它会被断言失败。在发布模式下,其行为是未定义的。</p>
<p>以下将会讨论有关查询各类型的细节。</p>
<h2><a class="anchor" id="QueryArray"></a>
查询Array</h2>
<p>缺省情况下,<code>SizeType</code>是<code>unsigned</code>的typedef。在多数系统中,Array最多能存储2^32-1个元素。</p>
<p>你可以用整数字面量访问元素,如<code>a[0]</code>、<code>a[1]</code>、<code>a[2]</code>。</p>
<p>Array与<code>std::vector</code>相似,除了使用索引,也可使用迭代器来访问所有元素。 </p><div class="fragment"><div class="line"><span class="keywordflow">for</span> (Value::ConstValueIterator itr = a.Begin(); itr != a.End(); ++itr)</div>
<div class="line">    printf(<span class="stringliteral">&quot;%d &quot;</span>, itr-&gt;GetInt());</div>
</div><!-- fragment --><p>还有一些熟悉的查询函数:</p><ul>
<li><code>SizeType Capacity() const</code></li>
<li><code>bool Empty() const</code></li>
</ul>
<h2><a class="anchor" id="QueryObject"></a>
查询Object</h2>
<p>和Array相似,我们可以用迭代器去访问所有Object成员:</p>
<div class="fragment"><div class="line"><span class="keyword">static</span> <span class="keyword">const</span> <span class="keywordtype">char</span>* kTypeNames[] = </div>
<div class="line">    { <span class="stringliteral">&quot;Null&quot;</span>, <span class="stringliteral">&quot;False&quot;</span>, <span class="stringliteral">&quot;True&quot;</span>, <span class="stringliteral">&quot;Object&quot;</span>, <span class="stringliteral">&quot;Array&quot;</span>, <span class="stringliteral">&quot;String&quot;</span>, <span class="stringliteral">&quot;Number&quot;</span> };</div>
<div class="line"></div>
<div class="line"><span class="keywordflow">for</span> (Value::ConstMemberIterator itr = document.MemberBegin();</div>
<div class="line">    itr != document.MemberEnd(); ++itr)</div>
<div class="line">{</div>
<div class="line">    printf(<span class="stringliteral">&quot;Type of member %s is %s\n&quot;</span>,</div>
<div class="line">        itr-&gt;name.GetString(), kTypeNames[itr-&gt;value.GetType()]);</div>
<div class="line">}</div>
</div><!-- fragment --><div class="fragment"><div class="line"><a class="code" href="namespacerapidjson.html#ae79a4751c1c460ff0de5ecc07874f3e4">Type</a> of member hello is String</div>
<div class="line"><a class="code" href="namespacerapidjson.html#ae79a4751c1c460ff0de5ecc07874f3e4">Type</a> of member t is True</div>
<div class="line"><a class="code" href="namespacerapidjson.html#ae79a4751c1c460ff0de5ecc07874f3e4">Type</a> of member f is False</div>
<div class="line"><a class="code" href="namespacerapidjson.html#ae79a4751c1c460ff0de5ecc07874f3e4">Type</a> of member n is Null</div>
<div class="line"><a class="code" href="namespacerapidjson.html#ae79a4751c1c460ff0de5ecc07874f3e4">Type</a> of member i is Number</div>
<div class="line"><a class="code" href="namespacerapidjson.html#ae79a4751c1c460ff0de5ecc07874f3e4">Type</a> of member pi is Number</div>
<div class="line"><a class="code" href="namespacerapidjson.html#ae79a4751c1c460ff0de5ecc07874f3e4">Type</a> of member a is Array</div>
</div><!-- fragment --><p>注意,当<code>operator[](const char*)</code>找不到成员,它会断言失败。</p>
<p>若我们不确定一个成员是否存在,便需要在调用<code>operator[](const char*)</code>前先调用<code>HasMember()</code>。然而,这会导致两次查找。更好的做法是调用<code>FindMember()</code>,它能同时检查成员是否存在并返回它的Value:</p>
<div class="fragment"><div class="line">Value::ConstMemberIterator itr = document.FindMember(<span class="stringliteral">&quot;hello&quot;</span>);</div>
<div class="line"><span class="keywordflow">if</span> (itr != document.MemberEnd())</div>
<div class="line">    printf(<span class="stringliteral">&quot;%s %s\n&quot;</span>, itr-&gt;value.GetString());</div>
</div><!-- fragment --><h2><a class="anchor" id="QueryNumber"></a>
查询Number</h2>
<p>JSON只提供一种数值类型──Number。数字可以是整数或实数。RFC 4627规定数字的范围由解析器指定。</p>
<p>由于C++提供多种整数及浮点数类型,DOM尝试尽量提供最广的范围及良好性能。</p>
<p>当解析一个Number时, 它会被存储在DOM之中,成为下列其中一个类型:</p>
<table class="doxtable">
<tr>
<th>类型 </th><th>描述  </th></tr>
<tr>
<td><code>unsigned</code> </td><td>32位无号整数 </td></tr>
<tr>
<td><code>int</code> </td><td>32位有号整数 </td></tr>
<tr>
<td><code>uint64_t</code> </td><td>64位无号整数 </td></tr>
<tr>
<td><code>int64_t</code> </td><td>64位有号整数 </td></tr>
<tr>
<td><code>double</code> </td><td>64位双精度浮点数 </td></tr>
</table>
<p>当查询一个Number时, 你可以检查该数字是否能以目标类型来提取:</p>
<table class="doxtable">
<tr>
<th>查检 </th><th>提取  </th></tr>
<tr>
<td><code>bool IsNumber()</code> </td><td>不适用 </td></tr>
<tr>
<td><code>bool IsUint()</code> </td><td><code>unsigned GetUint()</code> </td></tr>
<tr>
<td><code>bool IsInt()</code> </td><td><code>int GetInt()</code> </td></tr>
<tr>
<td><code>bool IsUint64()</code> </td><td><code>uint64_t GetUint64()</code> </td></tr>
<tr>
<td><code>bool IsInt64()</code> </td><td><code>int64_t GetInt64()</code> </td></tr>
<tr>
<td><code>bool IsDouble()</code> </td><td><code>double GetDouble()</code> </td></tr>
</table>
<p>注意,一个整数可能用几种类型来提取,而无需转换。例如,一个名为<code>x</code>的Value包含123,那么<code>x.IsInt() == x.IsUint() == x.IsInt64() == x.IsUint64() == true</code>。但如果一个名为<code>y</code>的Value包含-3000000000,那么仅会令<code>x.IsInt64() == true</code>。</p>
<p>当要提取Number类型,<code>GetDouble()</code>是会把内部整数的表示转换成<code>double</code>。注意<code>int</code> 和<code>unsigned</code>可以安全地转换至<code>double</code>,但<code>int64_t</code>及<code>uint64_t</code>可能会丧失精度(因为<code>double</code>的尾数只有52位)。</p>
<h2><a class="anchor" id="QueryString"></a>
查询String</h2>
<p>除了<code>GetString()</code>,<code>Value</code>类也有一个<code>GetStringLength()</code>。这里会解释个中原因。</p>
<p>根据RFC 4627,JSON String可包含Unicode字符<code>U+0000</code>,在JSON中会表示为<code>"\\u0000"</code>。问题是,C/C++通常使用空字符结尾字符串(null-terminated string),这种字符串把`<code>\0'</code>作为结束符号。</p>
<p>为了符合RFC 4627,RapidJSON支持包含<code>U+0000</code>的String。若你需要处理这些String,便可使用<code>GetStringLength()</code>去获得正确的字符串长度。</p>
<p>例如,当解析以下的JSON至<code>Document d</code>之后:</p>
<div class="fragment"><div class="line">{ <span class="stringliteral">&quot;s&quot;</span> :  <span class="stringliteral">&quot;a\u0000b&quot;</span> }</div>
</div><!-- fragment --><p> <code>"a\\u0000b"</code>值的正确长度应该是3。但<code>strlen()</code>会返回1。</p>
<p><code>GetStringLength()</code>也可以提高性能,因为用户可能需要调用<code>strlen()</code>去分配缓冲。</p>
<p>此外,<code>std::string</code>也支持这个构造函数:</p>
<div class="fragment"><div class="line">string(<span class="keyword">const</span> <span class="keywordtype">char</span>* s, <span class="keywordtype">size_t</span> count);</div>
</div><!-- fragment --><p>此构造函数接受字符串长度作为参数。它支持在字符串中存储空字符,也应该会有更好的性能。</p>
<h2>比较两个Value</h2>
<p>你可使用<code>==</code>及<code>!=</code>去比较两个Value。当且仅当两个Value的类型及内容相同,它们才当作相等。你也可以比较Value和它的原生类型值。以下是一个例子。</p>
<div class="fragment"><div class="line"><span class="keywordflow">if</span> (document[<span class="stringliteral">&quot;hello&quot;</span>] == document[<span class="stringliteral">&quot;n&quot;</span>]) <span class="comment">/*...*/</span>;    <span class="comment">// 比较两个值</span></div>
<div class="line"><span class="keywordflow">if</span> (document[<span class="stringliteral">&quot;hello&quot;</span>] == <span class="stringliteral">&quot;world&quot;</span>) <span class="comment">/*...*/</span>;          <span class="comment">// 与字符串家面量作比较</span></div>
<div class="line"><span class="keywordflow">if</span> (document[<span class="stringliteral">&quot;i&quot;</span>] != 123) <span class="comment">/*...*/</span>;                  <span class="comment">// 与整数作比较</span></div>
<div class="line"><span class="keywordflow">if</span> (document[<span class="stringliteral">&quot;pi&quot;</span>] != 3.14) <span class="comment">/*...*/</span>;                <span class="comment">// 与double作比较</span></div>
</div><!-- fragment --><p>Array/Object顺序以它们的元素/成员作比较。当且仅当它们的整个子树相等,它们才当作相等。</p>
<p>注意,现时若一个Object含有重复命名的成员,它与任何Object作比较都总会返回<code>false</code>。</p>
<h1><a class="anchor" id="CreateModifyValues"></a>
创建/修改值</h1>
<p>有多种方法去创建值。 当一个DOM树被创建或修改后,可使用<code>Writer</code>再次存储为JSON。</p>
<h2><a class="anchor" id="ChangeValueType"></a>
改变Value类型</h2>
<p>当使用默认构造函数创建一个Value或Document,它的类型便会是Null。要改变其类型,需调用<code>SetXXX()</code>或赋值操作,例如:</p>
<div class="fragment"><div class="line"><a class="code" href="namespacerapidjson.html#ace11b5b575baf1cccd5ba5f8586dcdc8">Document</a> d; <span class="comment">// Null</span></div>
<div class="line">d.SetObject();</div>
<div class="line"></div>
<div class="line"><a class="code" href="namespacerapidjson.html#aa65fc9fb381b2cbc54f98673eadd6505">Value</a> v;    <span class="comment">// Null</span></div>
<div class="line">v.SetInt(10);</div>
<div class="line">v = 10;     <span class="comment">// 简写,和上面的相同</span></div>
</div><!-- fragment --><h3>构造函数的各个重载</h3>
<p>几个类型也有重载构造函数:</p>
<div class="fragment"><div class="line"><a class="code" href="namespacerapidjson.html#aa65fc9fb381b2cbc54f98673eadd6505">Value</a> b(<span class="keyword">true</span>);    <span class="comment">// 调用Value(bool)</span></div>
<div class="line"><a class="code" href="namespacerapidjson.html#aa65fc9fb381b2cbc54f98673eadd6505">Value</a> i(-123);    <span class="comment">// 调用 Value(int)</span></div>
<div class="line"><a class="code" href="namespacerapidjson.html#aa65fc9fb381b2cbc54f98673eadd6505">Value</a> u(123u);    <span class="comment">// 调用Value(unsigned)</span></div>
<div class="line"><a class="code" href="namespacerapidjson.html#aa65fc9fb381b2cbc54f98673eadd6505">Value</a> d(1.5);     <span class="comment">// 调用Value(double)</span></div>
</div><!-- fragment --><p>要重建空Object或Array,可在默认构造函数后使用 <code>SetObject()</code>/<code>SetArray()</code>,或一次性使用<code>Value(Type)</code>:</p>
<div class="fragment"><div class="line"><a class="code" href="namespacerapidjson.html#aa65fc9fb381b2cbc54f98673eadd6505">Value</a> o(<a class="code" href="namespacerapidjson.html#ae79a4751c1c460ff0de5ecc07874f3e4acf030b422a32c3647c7c5973bd4dd0a9">kObjectType</a>);</div>
<div class="line"><a class="code" href="namespacerapidjson.html#aa65fc9fb381b2cbc54f98673eadd6505">Value</a> a(<a class="code" href="namespacerapidjson.html#ae79a4751c1c460ff0de5ecc07874f3e4a058c622e1e7d59419ae58b895cbce468">kArrayType</a>);</div>
</div><!-- fragment --><h2><a class="anchor" id="MoveSemantics"></a>
转移语意(Move Semantics)</h2>
<p>在设计RapidJSON时有一个非常特别的决定,就是Value赋值并不是把来源Value复制至目的Value,而是把把来源Value转移(move)至目的Value。例如:</p>
<div class="fragment"><div class="line"><a class="code" href="namespacerapidjson.html#aa65fc9fb381b2cbc54f98673eadd6505">Value</a> a(123);</div>
<div class="line"><a class="code" href="namespacerapidjson.html#aa65fc9fb381b2cbc54f98673eadd6505">Value</a> b(456);</div>
<div class="line">b = a;         <span class="comment">// a变成Null,b变成数字123。</span></div>
</div><!-- fragment --><div class="image">
<img src="move1.png" alt="move1.png"/>
<div class="caption">
使用移动语意赋值。</div></div>
<p> 为什么?此语意有何优点?</p>
<p>最简单的答案就是性能。对于固定大小的JSON类型(Number、True、False、Null),复制它们是简单快捷。然而,对于可变大小的JSON类型(String、Array、Object),复制它们会产生大量开销,而且这些开销常常不被察觉。尤其是当我们需要创建临时Object,把它复制至另一变量,然后再析构它。</p>
<p>例如,若使用正常*复制*语意:</p>
<div class="fragment"><div class="line"><a class="code" href="namespacerapidjson.html#aa65fc9fb381b2cbc54f98673eadd6505">Value</a> o(<a class="code" href="namespacerapidjson.html#ae79a4751c1c460ff0de5ecc07874f3e4acf030b422a32c3647c7c5973bd4dd0a9">kObjectType</a>);</div>
<div class="line">{</div>
<div class="line">    <a class="code" href="namespacerapidjson.html#aa65fc9fb381b2cbc54f98673eadd6505">Value</a> contacts(<a class="code" href="namespacerapidjson.html#ae79a4751c1c460ff0de5ecc07874f3e4a058c622e1e7d59419ae58b895cbce468">kArrayType</a>);</div>
<div class="line">    <span class="comment">// 把元素加进contacts数组。</span></div>
<div class="line">    <span class="comment">// ...</span></div>
<div class="line">    o.AddMember(<span class="stringliteral">&quot;contacts&quot;</span>, contacts, d.GetAllocator());  <span class="comment">// 深度复制contacts (可能有大量内存分配)</span></div>
<div class="line">    <span class="comment">// 析构contacts。</span></div>
<div class="line">}</div>
</div><!-- fragment --><div class="image">
<img src="move2.png" alt="move2.png"/>
<div class="caption">
复制语意产生大量的复制操作。</div></div>
<p> 那个<code>o</code> Object需要分配一个和contacts相同大小的缓冲区,对conacts做深度复制,并最终要析构contacts。这样会产生大量无必要的内存分配/释放,以及内存复制。</p>
<p>有一些方案可避免实质地复制这些数据,例如引用计数(reference counting)、垃圾回收(garbage collection, GC)。</p>
<p>为了使RapidJSON简单及快速,我们选择了对赋值采用*转移*语意。这方法与<code>std::auto_ptr</code>相似,都是在赋值时转移拥有权。转移快得多简单得多,只需要析构原来的Value,把来源<code>memcpy()</code>至目标,最后把来源设置为Null类型。</p>
<p>因此,使用转移语意后,上面的例子变成:</p>
<div class="fragment"><div class="line"><a class="code" href="namespacerapidjson.html#aa65fc9fb381b2cbc54f98673eadd6505">Value</a> o(<a class="code" href="namespacerapidjson.html#ae79a4751c1c460ff0de5ecc07874f3e4acf030b422a32c3647c7c5973bd4dd0a9">kObjectType</a>);</div>
<div class="line">{</div>
<div class="line">    <a class="code" href="namespacerapidjson.html#aa65fc9fb381b2cbc54f98673eadd6505">Value</a> contacts(<a class="code" href="namespacerapidjson.html#ae79a4751c1c460ff0de5ecc07874f3e4a058c622e1e7d59419ae58b895cbce468">kArrayType</a>);</div>
<div class="line">    <span class="comment">// adding elements to contacts array.</span></div>
<div class="line">    o.AddMember(<span class="stringliteral">&quot;contacts&quot;</span>, contacts, d.GetAllocator());  <span class="comment">// 只需 memcpy() contacts本身至新成员的Value(16字节)</span></div>
<div class="line">    <span class="comment">// contacts在这里变成Null。它的析构是平凡的。</span></div>
<div class="line">}</div>
</div><!-- fragment --><div class="image">
<img src="move3.png" alt="move3.png"/>
<div class="caption">
转移语意不需复制。</div></div>
<p> 在C++11中这称为转移赋值操作(move assignment operator)。由于RapidJSON 支持C++03,它在赋值操作采用转移语意,其它修改形函数如<code>AddMember()</code>, <code>PushBack()</code>也采用转移语意。</p>
<h3><a class="anchor" id="TemporaryValues"></a>
转移语意及临时值</h3>
<p>有时候,我们想直接构造一个Value并传递给一个“转移”函数(如<code>PushBack()</code>、<code>AddMember()</code>)。由于临时对象是不能转换为正常的Value引用,我们加入了一个方便的<code>Move()</code>函数:</p>
<div class="fragment"><div class="line"><a class="code" href="namespacerapidjson.html#aa65fc9fb381b2cbc54f98673eadd6505">Value</a> a(<a class="code" href="namespacerapidjson.html#ae79a4751c1c460ff0de5ecc07874f3e4a058c622e1e7d59419ae58b895cbce468">kArrayType</a>);</div>
<div class="line">Document::AllocatorType&amp; allocator = document.GetAllocator();</div>
<div class="line"><span class="comment">// a.PushBack(Value(42), allocator);       // 不能通过编译</span></div>
<div class="line">a.PushBack(<a class="code" href="namespacerapidjson.html#aa65fc9fb381b2cbc54f98673eadd6505">Value</a>().SetInt(42), allocator); <span class="comment">// fluent API</span></div>
<div class="line">a.PushBack(<a class="code" href="namespacerapidjson.html#aa65fc9fb381b2cbc54f98673eadd6505">Value</a>(42).Move(), allocator);   <span class="comment">// 和上一行相同</span></div>
</div><!-- fragment --><h2><a class="anchor" id="CreateString"></a>
创建String</h2>
<p>RapidJSON提供两个String的存储策略。</p>
<ol type="1">
<li>copy-string: 分配缓冲区,然后把来源数据复制至它。</li>
<li>const-string: 简单地储存字符串的指针。</li>
</ol>
<p>Copy-string总是安全的,因为它拥有数据的克隆。Const-string可用于存储字符串字面量,以及用于在DOM一节中将会提到的in-situ解析中。</p>
<p>为了让用户自定义内存分配方式,当一个操作可能需要内存分配时,RapidJSON要求用户传递一个allocator实例作为API参数。此设计避免了在每个Value存储allocator(或document)的指针。</p>
<p>因此,当我们把一个copy-string赋值时, 调用含有allocator的<code>SetString()</code>重载函数:</p>
<div class="fragment"><div class="line"><a class="code" href="namespacerapidjson.html#ace11b5b575baf1cccd5ba5f8586dcdc8">Document</a> document;</div>
<div class="line"><a class="code" href="namespacerapidjson.html#aa65fc9fb381b2cbc54f98673eadd6505">Value</a> author;</div>
<div class="line"><span class="keywordtype">char</span> buffer[10];</div>
<div class="line"><span class="keywordtype">int</span> len = sprintf(buffer, <span class="stringliteral">&quot;%s %s&quot;</span>, <span class="stringliteral">&quot;Milo&quot;</span>, <span class="stringliteral">&quot;Yip&quot;</span>); <span class="comment">// 动态创建的字符串。</span></div>
<div class="line">author.SetString(buffer, len, document.GetAllocator());</div>
<div class="line">memset(buffer, 0, <span class="keyword">sizeof</span>(buffer));</div>
<div class="line"><span class="comment">// 清空buffer后author.GetString() 仍然包含 &quot;Milo Yip&quot;</span></div>
</div><!-- fragment --><p>在此例子中,我们使用<code>Document</code>实例的allocator。这是使用RapidJSON时常用的惯用法。但你也可以用其他allocator实例。</p>
<p>另外,上面的<code>SetString()</code>需要长度参数。这个API能处理含有空字符的字符串。另一个<code>SetString()</code>重载函数没有长度参数,它假设输入是空字符结尾的,并会调用类似<code>strlen()</code>的函数去获取长度。</p>
<p>最后,对于字符串字面量或有安全生命周期的字符串,可以使用const-string版本的<code>SetString()</code>,它没有allocator参数。对于字符串家面量(或字符数组常量),只需简单地传递字面量,又安全又高效:</p>
<div class="fragment"><div class="line"><a class="code" href="namespacerapidjson.html#aa65fc9fb381b2cbc54f98673eadd6505">Value</a> s;</div>
<div class="line">s.SetString(<span class="stringliteral">&quot;rapidjson&quot;</span>);    <span class="comment">// 可包含空字符,长度在编译萁推导</span></div>
<div class="line">s = <span class="stringliteral">&quot;rapidjson&quot;</span>;             <span class="comment">// 上行的缩写</span></div>
</div><!-- fragment --><p>对于字符指针,RapidJSON需要作一个标记,代表它不复制也是安全的。可以使用<code>StringRef</code>函数:</p>
<div class="fragment"><div class="line"><span class="keyword">const</span> <span class="keywordtype">char</span> * cstr = getenv(<span class="stringliteral">&quot;USER&quot;</span>);</div>
<div class="line"><span class="keywordtype">size_t</span> cstr_len = ...;                 <span class="comment">// 如果有长度</span></div>
<div class="line"><a class="code" href="namespacerapidjson.html#aa65fc9fb381b2cbc54f98673eadd6505">Value</a> s;</div>
<div class="line"><span class="comment">// s.SetString(cstr);                  // 这不能通过编译</span></div>
<div class="line">s.SetString(<a class="code" href="structrapidjson_1_1_generic_string_ref.html#aa6b9fd9f6aa49405a574c362ba9af6b5">StringRef</a>(cstr));          <span class="comment">// 可以,假设它的生命周期案全,并且是以空字符结尾的</span></div>
<div class="line">s = <a class="code" href="structrapidjson_1_1_generic_string_ref.html#aa6b9fd9f6aa49405a574c362ba9af6b5">StringRef</a>(cstr);                   <span class="comment">// 上行的缩写</span></div>
<div class="line">s.SetString(<a class="code" href="structrapidjson_1_1_generic_string_ref.html#aa6b9fd9f6aa49405a574c362ba9af6b5">StringRef</a>(cstr, cstr_len));<span class="comment">// 更快,可处理空字符</span></div>
<div class="line">s = <a class="code" href="structrapidjson_1_1_generic_string_ref.html#aa6b9fd9f6aa49405a574c362ba9af6b5">StringRef</a>(cstr, cstr_len);         <span class="comment">// 上行的缩写</span></div>
</div><!-- fragment --><h2><a class="anchor" id="ModifyArray"></a>
修改Array</h2>
<p>Array类型的Value提供与<code>std::vector</code>相似的API。</p>
<ul>
<li><code>Clear()</code></li>
<li><code>Reserve(SizeType, Allocator&amp;)</code></li>
<li><code>Value&amp; PushBack(Value&amp;, Allocator&amp;)</code></li>
<li><code>template &lt;typename T&gt; GenericValue&amp; PushBack(T, Allocator&amp;)</code></li>
<li><code>Value&amp; PopBack()</code></li>
<li><code>ValueIterator Erase(ConstValueIterator pos)</code></li>
<li><code>ValueIterator Erase(ConstValueIterator first, ConstValueIterator last)</code></li>
</ul>
<p>注意,<code>Reserve(...)</code>及<code>PushBack(...)</code>可能会为数组元素分配内存,所以需要一个allocator。</p>
<p>以下是<code>PushBack()</code>的例子:</p>
<div class="fragment"><div class="line"><a class="code" href="namespacerapidjson.html#aa65fc9fb381b2cbc54f98673eadd6505">Value</a> a(<a class="code" href="namespacerapidjson.html#ae79a4751c1c460ff0de5ecc07874f3e4a058c622e1e7d59419ae58b895cbce468">kArrayType</a>);</div>
<div class="line">Document::AllocatorType&amp; allocator = document.GetAllocator();</div>
<div class="line"></div>
<div class="line"><span class="keywordflow">for</span> (<span class="keywordtype">int</span> i = 5; i &lt;= 10; i++)</div>
<div class="line">    a.PushBack(i, allocator);   <span class="comment">// 可能需要调用realloc()所以需要allocator</span></div>
<div class="line"></div>
<div class="line"><span class="comment">// 流畅接口(Fluent interface)</span></div>
<div class="line">a.PushBack(<span class="stringliteral">&quot;Lua&quot;</span>, allocator).PushBack(<span class="stringliteral">&quot;Mio&quot;</span>, allocator);</div>
</div><!-- fragment --><p>与STL不一样的是,<code>PushBack()</code>/<code>PopBack()</code>返回Array本身的引用。这称为流畅接口(_fluent interface_)。</p>
<p>如果你想在Array中加入一个非常量字符串,或是一个没有足够生命周期的字符串(见<a href="#CreateString">Create String</a>),你需要使用copy-string API去创建一个String。为了避免加入中间变量,可以就地使用一个<a href="#TemporaryValues">临时值</a>:</p>
<div class="fragment"><div class="line"><span class="comment">// 就地Value参数</span></div>
<div class="line">contact.PushBack(<a class="code" href="namespacerapidjson.html#aa65fc9fb381b2cbc54f98673eadd6505">Value</a>(<span class="stringliteral">&quot;copy&quot;</span>, document.GetAllocator()).Move(), <span class="comment">// copy string</span></div>
<div class="line">                 document.GetAllocator());</div>
<div class="line"></div>
<div class="line"><span class="comment">// 显式Value参数</span></div>
<div class="line"><a class="code" href="namespacerapidjson.html#aa65fc9fb381b2cbc54f98673eadd6505">Value</a> val(<span class="stringliteral">&quot;key&quot;</span>, document.GetAllocator()); <span class="comment">// copy string</span></div>
<div class="line">contact.PushBack(val, document.GetAllocator());</div>
</div><!-- fragment --><h2><a class="anchor" id="ModifyObject"></a>
修改Object</h2>
<p>Object是键值对的集合。每个键必须为String。要修改Object,方法是增加或移除成员。以下的API用来增加城员:</p>
<ul>
<li><code>Value&amp; AddMember(Value&amp;, Value&amp;, Allocator&amp; allocator)</code></li>
<li><code>Value&amp; AddMember(StringRefType, Value&amp;, Allocator&amp;)</code></li>
<li><code>template &lt;typename T&gt; Value&amp; AddMember(StringRefType, T value, Allocator&amp;)</code></li>
</ul>
<p>以下是一个例子。</p>
<div class="fragment"><div class="line"><a class="code" href="namespacerapidjson.html#aa65fc9fb381b2cbc54f98673eadd6505">Value</a> contact(kObject);</div>
<div class="line">contact.AddMember(<span class="stringliteral">&quot;name&quot;</span>, <span class="stringliteral">&quot;Milo&quot;</span>, document.GetAllocator());</div>
<div class="line">contact.AddMember(<span class="stringliteral">&quot;married&quot;</span>, <span class="keyword">true</span>, document.GetAllocator());</div>
</div><!-- fragment --><p>使用<code>StringRefType</code>作为name参数的重载版本与字符串的<code>SetString</code>的接口相似。 这些重载是为了避免复制<code>name</code>字符串,因为JSON object中经常会使用常数键名。</p>
<p>如果你需要从非常数字符串或生命周期不足的字符串创建键名(见<a href="#CreateString">创建String</a>),你需要使用copy-string API。为了避免中间变量,可以就地使用<a href="#TemporaryValues">临时值</a>:</p>
<div class="fragment"><div class="line"><span class="comment">// 就地Value参数</span></div>
<div class="line">contact.AddMember(<a class="code" href="namespacerapidjson.html#aa65fc9fb381b2cbc54f98673eadd6505">Value</a>(<span class="stringliteral">&quot;copy&quot;</span>, document.GetAllocator()).Move(), <span class="comment">// copy string</span></div>
<div class="line">                  <a class="code" href="namespacerapidjson.html#aa65fc9fb381b2cbc54f98673eadd6505">Value</a>().Move(),                                <span class="comment">// null value</span></div>
<div class="line">                  document.GetAllocator());</div>
<div class="line"></div>
<div class="line"><span class="comment">// 显式参数</span></div>
<div class="line"><a class="code" href="namespacerapidjson.html#aa65fc9fb381b2cbc54f98673eadd6505">Value</a> key(<span class="stringliteral">&quot;key&quot;</span>, document.GetAllocator()); <span class="comment">// copy string name</span></div>
<div class="line"><a class="code" href="namespacerapidjson.html#aa65fc9fb381b2cbc54f98673eadd6505">Value</a> val(42);                             <span class="comment">// 某Value</span></div>
<div class="line">contact.AddMember(key, val, document.GetAllocator());</div>
</div><!-- fragment --><p>移除成员有几个选择:</p>
<ul>
<li><code>bool RemoveMember(const Ch* name)</code>:使用键名来移除成员(线性时间复杂度)。</li>
<li><code>bool RemoveMember(const Value&amp; name)</code>:除了<code>name</code>是一个Value,和上一行相同。</li>
<li><code>MemberIterator RemoveMember(MemberIterator)</code>:使用迭代器移除成员(_常数_时间复杂度)。</li>
<li><code>MemberIterator EraseMember(MemberIterator)</code>:和上行相似但维持成员次序(线性时间复杂度)。</li>
<li><code>MemberIterator EraseMember(MemberIterator first, MemberIterator last)</code>:移除一个范围内的成员,维持次序(线性时间复杂度)。</li>
</ul>
<p><code>MemberIterator RemoveMember(MemberIterator)</code>使用了“转移最后”手法来达成常数时间复杂度。基本上就是析构迭代器位置的成员,然后把最后的成员转移至迭代器位置。因此,成员的次序会被改变。</p>
<h2><a class="anchor" id="DeepCopyValue"></a>
深复制Value</h2>
<p>若我们真的要复制一个DOM树,我们可使用两个APIs作深复制:含allocator的构造函数及<code>CopyFrom()</code>。</p>
<div class="fragment"><div class="line"><a class="code" href="namespacerapidjson.html#ace11b5b575baf1cccd5ba5f8586dcdc8">Document</a> d;</div>
<div class="line">Document::AllocatorType&amp; a = d.GetAllocator();</div>
<div class="line"><a class="code" href="namespacerapidjson.html#aa65fc9fb381b2cbc54f98673eadd6505">Value</a> v1(<span class="stringliteral">&quot;foo&quot;</span>);</div>
<div class="line"><span class="comment">// Value v2(v1); // 不容许</span></div>
<div class="line"></div>
<div class="line"><a class="code" href="namespacerapidjson.html#aa65fc9fb381b2cbc54f98673eadd6505">Value</a> v2(v1, a);                      <span class="comment">// 制造一个克隆</span></div>
<div class="line">assert(v1.IsString());                <span class="comment">// v1不变</span></div>
<div class="line">d.SetArray().PushBack(v1, a).PushBack(v2, a);</div>
<div class="line">assert(v1.IsNull() &amp;&amp; v2.IsNull());   <span class="comment">// 两个都转移动d</span></div>
<div class="line"></div>
<div class="line">v2.CopyFrom(d, a);                    <span class="comment">// 把整个document复制至v2</span></div>
<div class="line">assert(d.IsArray() &amp;&amp; d.Size() == 2); <span class="comment">// d不变</span></div>
<div class="line">v1.SetObject().AddMember(<span class="stringliteral">&quot;array&quot;</span>, v2, a);</div>
<div class="line">d.PushBack(v1, a);</div>
</div><!-- fragment --><h2><a class="anchor" id="SwapValues"></a>
交换Value</h2>
<p>RapidJSON也提供<code>Swap()</code>。</p>
<div class="fragment"><div class="line"><a class="code" href="namespacerapidjson.html#aa65fc9fb381b2cbc54f98673eadd6505">Value</a> a(123);</div>
<div class="line"><a class="code" href="namespacerapidjson.html#aa65fc9fb381b2cbc54f98673eadd6505">Value</a> b(<span class="stringliteral">&quot;Hello&quot;</span>);</div>
<div class="line">a.Swap(b);</div>
<div class="line">assert(a.IsString());</div>
<div class="line">assert(b.IsInt());</div>
</div><!-- fragment --><p>无论两棵DOM树有多复杂,交换是很快的(常数时间)。</p>
<h1><a class="anchor" id="WhatsNext"></a>
下一部分</h1>
<p>本教程展示了如何询查及修改DOM树。RapidJSON还有一个重要概念:</p>
<ol type="1">
<li><a class="el" href="md_doc_stream_8zh-cn.html">流</a> 是读写JSON的通道。流可以是内存字符串、文件流等。用户也可以自定义流。</li>
<li><a class="el" href="md_doc_encoding_8zh-cn.html">编码</a>定义在流或内存中使用的字符编码。RapidJSON也在内部提供Unicode转换及校验功能。</li>
<li><a class="el" href="md_doc_dom_8zh-cn.html">DOM</a>的基本功能已在本教程里介绍。还有更高级的功能,如原位(*in situ*)解析、其他解析选项及高级用法。</li>
<li><a class="el" href="md_doc_sax_8zh-cn.html">SAX</a> 是RapidJSON解析/生成功能的基础。学习使用<code>Reader</code>/<code>Writer</code>去实现更高性能的应用程序。也可以使用<code>PrettyWriter</code>去格式化JSON。</li>
<li><a class="el" href="md_doc_performance_8zh-cn.html">性能</a>展示一些我们做的及第三方的性能测试。</li>
<li>技术内幕讲述一些RapidJSON内部的设计及技术。</li>
</ol>
<p>你也可以参考常见问题、API文档、例子及单元测试。 </p>
</div></div><!-- contents -->
</div><!-- doc-content -->
<!-- HTML footer for doxygen 1.8.7-->
<!-- start footer part -->
<div id="nav-path" class="navpath"><!-- id is needed for treeview function! -->
  <ul>
  </ul>
</div>
<script type="text/javascript">
    /* * * CONFIGURATION VARIABLES * * */
    var disqus_shortname = 'rapidjson-doc';
    /* * * DON'T EDIT BELOW THIS LINE * * */
    (function() {
	    var dt = document.createElement('div');
	    dt.id = "disqus_thread";
	    (document.getElementsByClassName('contents')[0]).appendChild(dt);
        var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
        dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js';
        (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
    })();
</script>
</body>
</html>