3 This tutorial introduces the basics of the Document Object Model(DOM) API.
5 As shown in [Usage at a glance](@ref index), a JSON can be parsed into DOM, and then the DOM can be queried and modified easily, and finally be converted back to JSON.
9 # Value & Document {#ValueDocument}
11 Each JSON value is stored in a type called `Value`. A `Document`, representing the DOM, contains the root `Value` of the DOM tree. All public types and functions of RapidJSON are defined in the `rapidjson` namespace.
13 # Query Value {#QueryValue}
15 In this section, we will use excerpt of `example/tutorial/tutorial.cpp`.
17 Assumes we have a JSON stored in a C string (`const char* json`):
30 Parse it into a `Document`:
32 #include "rapidjson/document.h"
34 using namespace rapidjson;
41 The JSON is now parsed into `document` as a *DOM tree*:
43 ![DOM in the tutorial](diagram/tutorial.png)
45 Since the update to RFC 7159, the root of a conforming JSON document can be any JSON value. In earlier RFC 4627, only objects or arrays were allowed as root values. In this case, the root is an object.
47 assert(document.IsObject());
50 Let's query whether a `"hello"` member exists in the root object. Since a `Value` can contain different types of value, we may need to verify its type and use suitable API to obtain the value. In this example, `"hello"` member associates with a JSON string.
52 assert(document.HasMember("hello"));
53 assert(document["hello"].IsString());
54 printf("hello = %s\n", document["hello"].GetString());
61 JSON true/false values are represented as `bool`.
63 assert(document["t"].IsBool());
64 printf("t = %s\n", document["t"].GetBool() ? "true" : "false");
71 JSON null can be queryed by `IsNull()`.
73 printf("n = %s\n", document["n"].IsNull() ? "null" : "?");
80 JSON number type represents all numeric values. However, C++ needs more specific type for manipulation.
83 assert(document["i"].IsNumber());
85 // In this case, IsUint()/IsInt64()/IsUInt64() also return true.
86 assert(document["i"].IsInt());
87 printf("i = %d\n", document["i"].GetInt());
88 // Alternative (int)document["i"]
90 assert(document["pi"].IsNumber());
91 assert(document["pi"].IsDouble());
92 printf("pi = %g\n", document["pi"].GetDouble());
100 JSON array contains a number of elements.
102 // Using a reference for consecutive access is handy and faster.
103 const Value& a = document["a"];
105 for (SizeType i = 0; i < a.Size(); i++) // Uses SizeType instead of size_t
106 printf("a[%d] = %d\n", i, a[i].GetInt());
116 Note that, RapidJSON does not automatically convert values between JSON types. If a value is a string, it is invalid to call `GetInt()`, for example. In debug mode it will fail an assertion. In release mode, the behavior is undefined.
118 In the following, details about querying individual types are discussed.
120 ## Query Array {#QueryArray}
122 By default, `SizeType` is typedef of `unsigned`. In most systems, array is limited to store up to 2^32-1 elements.
124 You may access the elements in array by integer literal, for example, `a[0]`, `a[1]`, `a[2]`.
126 Array is similar to `std::vector`, instead of using indices, you may also use iterator to access all the elements.
128 for (Value::ConstValueIterator itr = a.Begin(); itr != a.End(); ++itr)
129 printf("%d ", itr->GetInt());
132 And other familiar query functions:
133 * `SizeType Capacity() const`
134 * `bool Empty() const`
136 ### Range-based For Loop (New in v1.1.0)
138 When C++11 is enabled, you can use range-based for loop to access all elements in an array.
141 for (auto& v : a.GetArray())
142 printf("%d ", v.GetInt());
145 ## Query Object {#QueryObject}
147 Similar to array, we can access all object members by iterator:
150 static const char* kTypeNames[] =
151 { "Null", "False", "True", "Object", "Array", "String", "Number" };
153 for (Value::ConstMemberIterator itr = document.MemberBegin();
154 itr != document.MemberEnd(); ++itr)
156 printf("Type of member %s is %s\n",
157 itr->name.GetString(), kTypeNames[itr->value.GetType()]);
162 Type of member hello is String
163 Type of member t is True
164 Type of member f is False
165 Type of member n is Null
166 Type of member i is Number
167 Type of member pi is Number
168 Type of member a is Array
171 Note that, when `operator[](const char*)` cannot find the member, it will fail an assertion.
173 If we are unsure whether a member exists, we need to call `HasMember()` before calling `operator[](const char*)`. However, this incurs two lookup. A better way is to call `FindMember()`, which can check the existence of member and obtain its value at once:
176 Value::ConstMemberIterator itr = document.FindMember("hello");
177 if (itr != document.MemberEnd())
178 printf("%s\n", itr->value.GetString());
181 ### Range-based For Loop (New in v1.1.0)
183 When C++11 is enabled, you can use range-based for loop to access all members in an object.
186 for (auto& m : document.GetObject())
187 printf("Type of member %s is %s\n",
188 m.name.GetString(), kTypeNames[m.value.GetType()]);
191 ## Querying Number {#QueryNumber}
193 JSON provide a single numerical type called Number. Number can be integer or real numbers. RFC 4627 says the range of Number is specified by parser.
195 As C++ provides several integer and floating point number types, the DOM tries to handle these with widest possible range and good performance.
197 When a Number is parsed, it is stored in the DOM as either one of the following type:
200 -----------|---------------------------------------
201 `unsigned` | 32-bit unsigned integer
202 `int` | 32-bit signed integer
203 `uint64_t` | 64-bit unsigned integer
204 `int64_t` | 64-bit signed integer
205 `double` | 64-bit double precision floating point
207 When querying a number, you can check whether the number can be obtained as target type:
210 ------------------|---------------------
211 `bool IsNumber()` | N/A
212 `bool IsUint()` | `unsigned GetUint()`
213 `bool IsInt()` | `int GetInt()`
214 `bool IsUint64()` | `uint64_t GetUint64()`
215 `bool IsInt64()` | `int64_t GetInt64()`
216 `bool IsDouble()` | `double GetDouble()`
218 Note that, an integer value may be obtained in various ways without conversion. For example, A value `x` containing 123 will make `x.IsInt() == x.IsUint() == x.IsInt64() == x.IsUint64() == true`. But a value `y` containing -3000000000 will only makes `x.IsInt64() == true`.
220 When obtaining the numeric values, `GetDouble()` will convert internal integer representation to a `double`. Note that, `int` and `unsigned` can be safely convert to `double`, but `int64_t` and `uint64_t` may lose precision (since mantissa of `double` is only 52-bits).
222 ## Query String {#QueryString}
224 In addition to `GetString()`, the `Value` class also contains `GetStringLength()`. Here explains why.
226 According to RFC 4627, JSON strings can contain Unicode character `U+0000`, which must be escaped as `"\u0000"`. The problem is that, C/C++ often uses null-terminated string, which treats ``\0'` as the terminator symbol.
228 To conform RFC 4627, RapidJSON supports string containing `U+0000`. If you need to handle this, you can use `GetStringLength()` API to obtain the correct length of string.
230 For example, after parsing a the following JSON to `Document d`:
235 The correct length of the value `"a\u0000b"` is 3. But `strlen()` returns 1.
237 `GetStringLength()` can also improve performance, as user may often need to call `strlen()` for allocating buffer.
239 Besides, `std::string` also support a constructor:
242 string(const char* s, size_t count);
245 which accepts the length of string as parameter. This constructor supports storing null character within the string, and should also provide better performance.
249 You can use `==` and `!=` to compare values. Two values are equal if and only if they are have same type and contents. You can also compare values with primitive types. Here is an example.
252 if (document["hello"] == document["n"]) /*...*/; // Compare values
253 if (document["hello"] == "world") /*...*/; // Compare value with literal string
254 if (document["i"] != 123) /*...*/; // Compare with integers
255 if (document["pi"] != 3.14) /*...*/; // Compare with double.
258 Array/object compares their elements/members in order. They are equal if and only if their whole subtrees are equal.
260 Note that, currently if an object contains duplicated named member, comparing equality with any object is always `false`.
262 # Create/Modify Values {#CreateModifyValues}
264 There are several ways to create values. After a DOM tree is created and/or modified, it can be saved as JSON again using `Writer`.
266 ## Change Value Type {#ChangeValueType}
267 When creating a Value or Document by default constructor, its type is Null. To change its type, call `SetXXX()` or assignment operator, for example:
275 v = 10; // Shortcut, same as above
278 ### Overloaded Constructors
279 There are also overloaded constructors for several types:
282 Value b(true); // calls Value(bool)
283 Value i(-123); // calls Value(int)
284 Value u(123u); // calls Value(unsigned)
285 Value d(1.5); // calls Value(double)
288 To create empty object or array, you may use `SetObject()`/`SetArray()` after default constructor, or using the `Value(Type)` in one shot:
291 Value o(kObjectType);
295 ## Move Semantics {#MoveSemantics}
297 A very special decision during design of RapidJSON is that, assignment of value does not copy the source value to destination value. Instead, the value from source is moved to the destination. For example,
302 b = a; // a becomes a Null value, b becomes number 123.
305 ![Assignment with move semantics.](diagram/move1.png)
307 Why? What is the advantage of this semantics?
309 The simple answer is performance. For fixed size JSON types (Number, True, False, Null), copying them is fast and easy. However, For variable size JSON types (String, Array, Object), copying them will incur a lot of overheads. And these overheads are often unnoticed. Especially when we need to create temporary object, copy it to another variable, and then destruct it.
311 For example, if normal *copy* semantics was used:
315 Value o(kObjectType);
317 Value contacts(kArrayType);
318 // adding elements to contacts array.
320 o.AddMember("contacts", contacts, d.GetAllocator()); // deep clone contacts (may be with lots of allocations)
321 // destruct contacts.
325 ![Copy semantics makes a lots of copy operations.](diagram/move2.png)
327 The object `o` needs to allocate a buffer of same size as contacts, makes a deep clone of it, and then finally contacts is destructed. This will incur a lot of unnecessary allocations/deallocations and memory copying.
329 There are solutions to prevent actual copying these data, such as reference counting and garbage collection(GC).
331 To make RapidJSON simple and fast, we chose to use *move* semantics for assignment. It is similar to `std::auto_ptr` which transfer ownership during assignment. Move is much faster and simpler, it just destructs the original value, `memcpy()` the source to destination, and finally sets the source as Null type.
333 So, with move semantics, the above example becomes:
337 Value o(kObjectType);
339 Value contacts(kArrayType);
340 // adding elements to contacts array.
341 o.AddMember("contacts", contacts, d.GetAllocator()); // just memcpy() of contacts itself to the value of new member (16 bytes)
342 // contacts became Null here. Its destruction is trivial.
346 ![Move semantics makes no copying.](diagram/move3.png)
348 This is called move assignment operator in C++11. As RapidJSON supports C++03, it adopts move semantics using assignment operator, and all other modifying function like `AddMember()`, `PushBack()`.
350 ### Move semantics and temporary values {#TemporaryValues}
352 Sometimes, it is convenient to construct a Value in place, before passing it to one of the "moving" functions, like `PushBack()` or `AddMember()`. As temporary objects can't be converted to proper Value references, the convenience function `Move()` is available:
356 Document::AllocatorType& allocator = document.GetAllocator();
357 // a.PushBack(Value(42), allocator); // will not compile
358 a.PushBack(Value().SetInt(42), allocator); // fluent API
359 a.PushBack(Value(42).Move(), allocator); // same as above
362 ## Create String {#CreateString}
363 RapidJSON provide two strategies for storing string.
365 1. copy-string: allocates a buffer, and then copy the source data into it.
366 2. const-string: simply store a pointer of string.
368 Copy-string is always safe because it owns a copy of the data. Const-string can be used for storing string literal, and in-situ parsing which we will mentioned in Document section.
370 To make memory allocation customizable, RapidJSON requires user to pass an instance of allocator, whenever an operation may require allocation. This design is needed to prevent storing a allocator (or Document) pointer per Value.
372 Therefore, when we assign a copy-string, we call this overloaded `SetString()` with allocator:
378 int len = sprintf(buffer, "%s %s", "Milo", "Yip"); // dynamically created string.
379 author.SetString(buffer, len, document.GetAllocator());
380 memset(buffer, 0, sizeof(buffer));
381 // author.GetString() still contains "Milo Yip" after buffer is destroyed
384 In this example, we get the allocator from a `Document` instance. This is a common idiom when using RapidJSON. But you may use other instances of allocator.
386 Besides, the above `SetString()` requires length. This can handle null characters within a string. There is another `SetString()` overloaded function without the length parameter. And it assumes the input is null-terminated and calls a `strlen()`-like function to obtain the length.
388 Finally, for string literal or string with safe life-cycle can use const-string version of `SetString()`, which lacks allocator parameter. For string literals (or constant character arrays), simply passing the literal as parameter is safe and efficient:
392 s.SetString("rapidjson"); // can contain null character, length derived at compile time
393 s = "rapidjson"; // shortcut, same as above
396 For character pointer, the RapidJSON requires to mark it as safe before using it without copying. This can be achieved by using the `StringRef` function:
399 const char * cstr = getenv("USER");
400 size_t cstr_len = ...; // in case length is available
402 // s.SetString(cstr); // will not compile
403 s.SetString(StringRef(cstr)); // ok, assume safe lifetime, null-terminated
404 s = StringRef(cstr); // shortcut, same as above
405 s.SetString(StringRef(cstr,cstr_len)); // faster, can contain null character
406 s = StringRef(cstr,cstr_len); // shortcut, same as above
410 ## Modify Array {#ModifyArray}
411 Value with array type provides similar APIs as `std::vector`.
414 * `Reserve(SizeType, Allocator&)`
415 * `Value& PushBack(Value&, Allocator&)`
416 * `template <typename T> GenericValue& PushBack(T, Allocator&)`
418 * `ValueIterator Erase(ConstValueIterator pos)`
419 * `ValueIterator Erase(ConstValueIterator first, ConstValueIterator last)`
421 Note that, `Reserve(...)` and `PushBack(...)` may allocate memory for the array elements, therefore require an allocator.
423 Here is an example of `PushBack()`:
427 Document::AllocatorType& allocator = document.GetAllocator();
429 for (int i = 5; i <= 10; i++)
430 a.PushBack(i, allocator); // allocator is needed for potential realloc().
433 a.PushBack("Lua", allocator).PushBack("Mio", allocator);
436 Differs from STL, `PushBack()`/`PopBack()` returns the array reference itself. This is called _fluent interface_.
438 If you want to add a non-constant string or a string without sufficient lifetime (see [Create String](#CreateString)) to the array, you need to create a string Value by using the copy-string API. To avoid the need for an intermediate variable, you can use a [temporary value](#TemporaryValues) in place:
441 // in-place Value parameter
442 contact.PushBack(Value("copy", document.GetAllocator()).Move(), // copy string
443 document.GetAllocator());
445 // explicit parameters
446 Value val("key", document.GetAllocator()); // copy string
447 contact.PushBack(val, document.GetAllocator());
450 ## Modify Object {#ModifyObject}
451 Object is a collection of key-value pairs (members). Each key must be a string value. To modify an object, either add or remove members. THe following APIs are for adding members:
453 * `Value& AddMember(Value&, Value&, Allocator& allocator)`
454 * `Value& AddMember(StringRefType, Value&, Allocator&)`
455 * `template <typename T> Value& AddMember(StringRefType, T value, Allocator&)`
460 Value contact(kObject);
461 contact.AddMember("name", "Milo", document.GetAllocator());
462 contact.AddMember("married", true, document.GetAllocator());
465 The name parameter with `StringRefType` is similar to the interface of `SetString` function for string values. These overloads are used to avoid the need for copying the `name` string, as constant key names are very common in JSON objects.
467 If you need to create a name from a non-constant string or a string without sufficient lifetime (see [Create String](#CreateString)), you need to create a string Value by using the copy-string API. To avoid the need for an intermediate variable, you can use a [temporary value](#TemporaryValues) in place:
470 // in-place Value parameter
471 contact.AddMember(Value("copy", document.GetAllocator()).Move(), // copy string
472 Value().Move(), // null value
473 document.GetAllocator());
475 // explicit parameters
476 Value key("key", document.GetAllocator()); // copy string name
477 Value val(42); // some value
478 contact.AddMember(key, val, document.GetAllocator());
481 For removing members, there are several choices:
483 * `bool RemoveMember(const Ch* name)`: Remove a member by search its name (linear time complexity).
484 * `bool RemoveMember(const Value& name)`: same as above but `name` is a Value.
485 * `MemberIterator RemoveMember(MemberIterator)`: Remove a member by iterator (_constant_ time complexity).
486 * `MemberIterator EraseMember(MemberIterator)`: similar to the above but it preserves order of members (linear time complexity).
487 * `MemberIterator EraseMember(MemberIterator first, MemberIterator last)`: remove a range of members, preserves order (linear time complexity).
489 `MemberIterator RemoveMember(MemberIterator)` uses a "move-last" trick to achieve constant time complexity. Basically the member at iterator is destructed, and then the last element is moved to that position. So the order of the remaining members are changed.
491 ## Deep Copy Value {#DeepCopyValue}
492 If we really need to copy a DOM tree, we can use two APIs for deep copy: constructor with allocator, and `CopyFrom()`.
496 Document::AllocatorType& a = d.GetAllocator();
498 // Value v2(v1); // not allowed
500 Value v2(v1, a); // make a copy
501 assert(v1.IsString()); // v1 untouched
502 d.SetArray().PushBack(v1, a).PushBack(v2, a);
503 assert(v1.IsNull() && v2.IsNull()); // both moved to d
505 v2.CopyFrom(d, a); // copy whole document to v2
506 assert(d.IsArray() && d.Size() == 2); // d untouched
507 v1.SetObject().AddMember("array", v2, a);
511 ## Swap Values {#SwapValues}
513 `Swap()` is also provided.
519 assert(a.IsString());
523 Swapping two DOM trees is fast (constant time), despite the complexity of the trees.
525 # What's next {#WhatsNext}
527 This tutorial shows the basics of DOM tree query and manipulation. There are several important concepts in RapidJSON:
529 1. [Streams](doc/stream.md) are channels for reading/writing JSON, which can be a in-memory string, or file stream, etc. User can also create their streams.
530 2. [Encoding](doc/encoding.md) defines which character encoding is used in streams and memory. RapidJSON also provide Unicode conversion/validation internally.
531 3. [DOM](doc/dom.md)'s basics are already covered in this tutorial. Uncover more advanced features such as *in situ* parsing, other parsing options and advanced usages.
532 4. [SAX](doc/sax.md) is the foundation of parsing/generating facility in RapidJSON. Learn how to use `Reader`/`Writer` to implement even faster applications. Also try `PrettyWriter` to format the JSON.
533 5. [Performance](doc/performance.md) shows some in-house and third-party benchmarks.
534 6. [Internals](doc/internals.md) describes some internal designs and techniques of RapidJSON.
536 You may also refer to the [FAQ](doc/faq.md), API documentation, examples and unit tests.