Home | History | Annotate | Download | only in doc
      1 # Pointer
      2 
      3 ## Status: experimental, shall be included in v1.1
      4 
      5 JSON Pointer is a standardized ([RFC6901]) way to select a value inside a JSON Document (DOM). This can be analogous to XPath for XML document. However, JSON Pointer is much simpler, and a single JSON Pointer only pointed to a single value.
      6 
      7 Using RapidJSON's implementation of JSON Pointer can simplify some manipulations of the DOM.
      8 
      9 [TOC]
     10 
     11 # JSON Pointer {#JsonPointer}
     12 
     13 A JSON Pointer is a list of zero-to-many tokens, each prefixed by `/`. Each token can be a string or a number. For example, given a JSON:
     14 ~~~javascript
     15 {
     16     "foo" : ["bar", "baz"],
     17     "pi" : 3.1416
     18 }
     19 ~~~
     20 
     21 The following JSON Pointers resolve this JSON as:
     22 
     23 1. `"/foo"`  `[ "bar", "baz" ]`
     24 2. `"/foo/0"`  `"bar"`
     25 3. `"/foo/1"`  `"baz"`
     26 4. `"/pi"`  `3.1416`
     27 
     28 Note that, an empty JSON Pointer `""` (zero token) resolves to the whole JSON.
     29 
     30 # Basic Usage {#BasicUsage}
     31 
     32 The following example code is self-explanatory.
     33 
     34 ~~~cpp
     35 #include "rapidjson/pointer.h"
     36 
     37 // ...
     38 Document d;
     39 
     40 // Create DOM by Set()
     41 Pointer("/project").Set(d, "RapidJSON");
     42 Pointer("/stars").Set(d, 10);
     43 
     44 // { "project" : "RapidJSON", "stars" : 10 }
     45 
     46 // Access DOM by Get(). It return nullptr if the value does not exist.
     47 if (Value* stars = Pointer("/stars").Get(d))
     48     stars->SetInt(stars->GetInt() + 1);
     49 
     50 // { "project" : "RapidJSON", "stars" : 11 }
     51 
     52 // Set() and Create() automatically generate parents if not exist.
     53 Pointer("/a/b/0").Create(d);
     54 
     55 // { "project" : "RapidJSON", "stars" : 11, "a" : { "b" : [ null ] } }
     56 
     57 // GetWithDefault() returns reference. And it deep clones the default value.
     58 Value& hello = Pointer("/hello").GetWithDefault(d, "world");
     59 
     60 // { "project" : "RapidJSON", "stars" : 11, "a" : { "b" : [ null ] }, "hello" : "world" }
     61 
     62 // Swap() is similar to Set()
     63 Value x("C++");
     64 Pointer("/hello").Swap(d, x);
     65 
     66 // { "project" : "RapidJSON", "stars" : 11, "a" : { "b" : [ null ] }, "hello" : "C++" }
     67 // x becomes "world"
     68 
     69 // Erase a member or element, return true if the value exists
     70 bool success = Pointer("/a").Erase(d);
     71 assert(success);
     72 
     73 // { "project" : "RapidJSON", "stars" : 10 }
     74 ~~~
     75 
     76 # Helper Functions {#HelperFunctions}
     77 
     78 Since object-oriented calling convention may be non-intuitive, RapidJSON also provides helper functions, which just wrap the member functions with free-functions.
     79 
     80 The following example does exactly the same as the above one.
     81 
     82 ~~~cpp
     83 Document d;
     84 
     85 SetValueByPointer(d, "/project", "RapidJSON");
     86 SetValueByPointer(d, "/stars", 10);
     87 
     88 if (Value* stars = GetValueByPointer(d, "/stars"))
     89     stars->SetInt(stars->GetInt() + 1);
     90 
     91 CreateValueByPointer(d, "/a/b/0");
     92 
     93 Value& hello = GetValueByPointerWithDefault(d, "/hello", "world");
     94 
     95 Value x("C++");
     96 SwapValueByPointer(d, "/hello", x);
     97 
     98 bool success = EraseValueByPointer(d, "/a");
     99 assert(success);
    100 ~~~
    101 
    102 The conventions are shown here for comparison:
    103 
    104 1. `Pointer(source).<Method>(root, ...)`
    105 2. `<Method>ValueByPointer(root, Pointer(source), ...)`
    106 3. `<Method>ValueByPointer(root, source, ...)`
    107 
    108 # Resolving Pointer {#ResolvingPointer}
    109 
    110 `Pointer::Get()` or `GetValueByPointer()` function does not modify the DOM. If the tokens cannot match a value in the DOM, it returns `nullptr`. User can use this to check whether a value exists.
    111 
    112 Note that, numerical tokens can represent an array index or member name. The resolving process will match the values according to the types of value.
    113 
    114 ~~~javascript
    115 {
    116     "0" : 123,
    117     "1" : [456]
    118 }
    119 ~~~
    120 
    121 1. `"/0"`  `123`
    122 2. `"/1/0"`  `456`
    123 
    124 The token `"0"` is treated as member name in the first pointer. It is treated as an array index in the second pointer.
    125 
    126 The other functions, including `Create()`, `GetWithDefault()`, `Set()` and `Swap()`, will change the DOM. These functions will always succeed. They will create the parent values if they do not exist. If the parent values do not match the tokens, they will also be forced to change their type. Changing the type also mean fully removal of that DOM subtree.
    127 
    128 Parsing the above JSON into `d`, 
    129 
    130 ~~~cpp
    131 SetValueByPointer(d, "1/a", 789); // { "0" : 123, "1" : { "a" : 789 } }
    132 ~~~
    133 
    134 ## Resolving Minus Sign Token
    135 
    136 Besides, [RFC6901] defines a special token `-` (single minus sign), which represents the pass-the-end element of an array. `Get()` only treats this token as a member name '"-"'. Yet the other functions can resolve this for array, equivalent to calling `Value::PushBack()` to the array.
    137 
    138 ~~~cpp
    139 Document d;
    140 d.Parse("{\"foo\":[123]}");
    141 SetValueByPointer(d, "/foo/-", 456); // { "foo" : [123, 456] }
    142 SetValueByPointer(d, "/-", 789);    // { "foo" : [123, 456], "-" : 789 }
    143 ~~~
    144 
    145 ## Resolving Document and Value
    146 
    147 When using `p.Get(root)` or `GetValueByPointer(root, p)`, `root` is a (const) `Value&`. That means, it can be a subtree of the DOM.
    148 
    149 The other functions have two groups of signature. One group uses `Document& document` as parameter, another one uses `Value& root`. The first group uses `document.GetAllocator()` for creating values. And the second group needs user to supply an allocator, like the functions in DOM.
    150 
    151 All examples above do not require an allocator parameter, because the parameter is a `Document&`. But if you want to resolve a pointer to a subtree. You need to supply it as in the following example:
    152 
    153 ~~~cpp
    154 class Person {
    155 public:
    156     Person() {
    157         document_ = new Document();
    158         // CreateValueByPointer() here no need allocator
    159         SetLocation(CreateValueByPointer(*document_, "/residence"), ...);
    160         SetLocation(CreateValueByPointer(*document_, "/office"), ...);
    161     };
    162 
    163 private:
    164     void SetLocation(Value& location, const char* country, const char* addresses[2]) {
    165         Value::Allocator& a = document_->GetAllocator();
    166         // SetValueByPointer() here need allocator
    167         SetValueByPointer(location, "/country", country, a);
    168         SetValueByPointer(location, "/address/0", address[0], a);
    169         SetValueByPointer(location, "/address/1", address[1], a);
    170     }
    171 
    172     // ...
    173 
    174     Document* document_;
    175 };
    176 ~~~
    177 
    178 `Erase()` or `EraseValueByPointer()` does not need allocator. And they return `true` if the value is erased successfully.
    179 
    180 # Error Handling {#ErrorHandling}
    181 
    182 A `Pointer` parses a source string in its constructor. If there is parsing error, `Pointer::IsValid()` returns false. And you can use `Pointer::GetParseErrorCode()` and `GetParseErrorOffset()` to retrieve the error information.
    183 
    184 Note that, all resolving functions assumes valid pointer. Resolving with an invalid pointer causes assertion failure.
    185 
    186 # URI Fragment Representation {#URIFragment}
    187 
    188 In addition to the string representation of JSON pointer that we are using till now, [RFC6901] also defines the URI fragment representation of JSON pointer. URI fragment is specified in [RFC3986] "Uniform Resource Identifier (URI): Generic Syntax".
    189 
    190 The main differences are that a the URI fragment always has a `#` (pound sign) in the beginning, and some characters are encoded by percent-encoding in UTF-8 sequence. For example, the following table shows different C/C++ string literals of different representations.
    191 
    192 String Representation | URI Fragment Representation | Pointer Tokens (UTF-8)
    193 ----------------------|-----------------------------|------------------------
    194 `"/foo/0"`            | `"#/foo/0"`                 | `{"foo", 0}`
    195 `"/a~1b"`             | `"#/a~1b"`                  | `{"a/b"}`
    196 `"/m~0n"`             | `"#/m~0n"`                  | `{"m~n"}`
    197 `"/ "`                | `"#/%20"`                   | `{" "}`
    198 `"/\0"`               | `"#/%00"`                   | `{"\0"}`
    199 `"/"`                | `"#/%E2%82%AC"`             | `{""}`
    200 
    201 RapidJSON fully support URI fragment representation. It automatically detects the pound sign during parsing.
    202 
    203 # Stringify
    204 
    205 You may also stringify a `Pointer` to a string or other output streams. This can be done by:
    206 
    207 ~~~
    208 Pointer p(...);
    209 StringBuffer sb;
    210 p.Stringify(sb);
    211 std::cout << sb.GetString() << std::endl;
    212 ~~~
    213 
    214 It can also stringify to URI fragment reprsentation by `StringifyUriFragment()`.
    215 
    216 # User-Supplied Tokens {#UserSuppliedTokens}
    217 
    218 If a pointer will be resolved multiple times, it should be construct once, and then apply it to different DOMs or in different times. This reduce time and memory allocation for constructing `Pointer` multiple times.
    219 
    220 We can go one step further, to completely eliminate the parsing process and dynamic memory allocation, we can establish the token array directly:
    221 
    222 ~~~cpp
    223 #define NAME(s) { s, sizeof(s) / sizeof(s[0]) - 1, kPointerInvalidIndex }
    224 #define INDEX(i) { #i, sizeof(#i) - 1, i }
    225 
    226 static const Pointer::Token kTokens[] = { NAME("foo"), INDEX(123) };
    227 static const Pointer p(kTokens, sizeof(kTokens) / sizeof(kTokens[0]));
    228 // Equivalent to static const Pointer p("/foo/123");
    229 ~~~
    230 
    231 This may be useful for memory constrained systems.
    232 
    233 [RFC3986]: https://tools.ietf.org/html/rfc3986
    234 [RFC6901]: https://tools.ietf.org/html/rfc6901
    235