Introduction to RapidJSON: introduction to hands-on examples

RapidJSON benefits

  • Cross platform
    Compilers: Visual Studio, gcc, clang, etc
    Architecture: x86, x64, ARM, etc
    Operating system: Windows, Mac OS X, Linux, iOS, Android, etc

  • easy to install
    A library with only header files. Just copy the header file to your project.

  • Independent, minimum dependency
    It does not need to rely on STL, BOOST, etc.
    Only < cstdio >, < cstdlib >, < CString >, < inttypes h>, <new>, <stdint. h>.

  • C + + exceptions and RTTI are not used

  • High performance
    Use templates and inline functions to reduce function call overhead.
    Internally optimized Grisu2 and floating-point parsing implementation.
    Optional SSE2 / SSE4 2 support.

RapidJSON tutorial

1. Value and Document

Each JSON Value is stored as a Value class. A Value class can contain different types of values, while the Document class represents the whole DOM, which stores the root Value of a DOM tree.
Define a JSON string and parse it into a Document:

#include "rapidjson/document.h"
 
using namespace rapidjson;
 
// ...

const char* json = 
{
    "hello": "world",
    "t": true ,
    "f": false,
    "n": null,
    "i": 123,
    "pi": 3.1416,
    "a": [1, 2, 3, 4]
}

Document document;
document.Parse(json);
//JSON.parse()
//When receiving server data, it is generally a string.
//You can use JSON Parse() converts the data into JavaScript objects.

Now, the JSON will be parsed into the document and become a DOM tree:

2. Query Value

//The root is an Object. Verify its type
assert(document.IsObject());



//Check whether there is a "hello" member in the root Object
//In this example, the "hello" member is associated with a JSON String
assert(document.HasMember("hello"));
assert(document["hello"].IsString());
printf("hello = %s\n", document["hello"].GetString());
//Print result: world



//JSON True/False values are represented by bool.
assert(document["t"].IsBool());
printf("t = %s\n", document["t"].GetBool() ? "true" : "false");
//Print result: true



//JSON Null values can be queried with IsNull().
printf("n = %s\n", document["n"].IsNull() ? "null" : "?");
//Print result: null



//The JSON Number type represents all values
//However, C + + needs to use more specialized types
assert(document["i"].IsNumber());
assert(document["i"].IsInt());// In this case, IsUint()/IsInt64()/IsUint64() will also return true
printf("i = %d\n", document["i"].GetInt());
//Print result: i = 123
assert(document["pi"].IsNumber());
assert(document["pi"].IsDouble());
printf("pi = %g\n", document["pi"].GetDouble());
//Print result: pi = 3.1416



// Using references for continuous access is more convenient and efficient.
const Value& a = document["a"];
assert(a.IsArray());
for (SizeType i = 0; i < a.Size(); i++) // Use SizeType instead of size_t
        printf("a[%d] = %d\n", i, a[i].GetInt());
//Print result: a[0] = 1
//         a[1] = 2
//         a[2] = 3
//         a[3] = 4



//Use iterators to access all Object members:
static const char* kTypeNames[] = 
{
    "Null", "False", "True", "Object", "Array", "String", "Number"
};



//If we are not sure whether a member exists, we need to call 
//1,HasMember()
//2,operator[](const char*) 
//However, this results in two lookups
//A better approach is to call FindMember()
//It can also check whether the member exists and return its Value
Value::ConstMemberIterator itr = document.FindMember("hello");
if (itr != document.MemberEnd())
    printf("%s\n", itr->value.GetString());
//Print result: world


 
for (Value::ConstMemberIterator itr = document.MemberBegin();
    itr != document.MemberEnd(); ++itr)
{
    printf("Type of member %s is %s\n",
        itr->name.GetString(), 
        kTypeNames[itr->value.GetType()]);
}
//Print result: Type of member hello is String
//         Type of member t is True
//         Type of member f is False
//         Type of member n is Null
//         Type of member i is Number
//         Type of member pi is Number
//         Type of member a is Array



//When using the C++11 function, you can use the range for loop to access all members in the Object.
for (auto& m : document.GetObject())
    printf("Type of member %s is %s\n",
        m.name.GetString(), kTypeNames[m.value.GetType()]);
//Print result: Type of member hello is String
//         Type of member t is True
//         Type of member f is False
//         Type of member n is Null
//         Type of member i is Number
//         Type of member pi is Number
//         Type of member a is Array



//Compare two values
//Use = = and= To compare two values
//Two values are considered equal if and only if they have the same type and content
if (document["hello"] == document["n"]) /*...*/;    // Compare two values
if (document["hello"] == "world") /*...*/;          // Compare with string literal
if (document["i"] != 123) /*...*/;                  // Compare with integer
if (document["pi"] != 3.14) /*...*/;                // Compare with double

JSON provides only one numeric type - Number. Numbers can be integers or real numbers, while C + + provides many types of integers and floating-point numbers. When querying a Number, you can check whether the Number can be extracted with the target type:
checkextract
bool IsNumber()Not applicable
bool IsUint()unsigned GetUint()
bool IsInt()int GetInt()
bool IsUint64()uint64_t GetUint64()
bool IsInt64()int64_t GetInt64()
bool IsDouble()double GetDouble()

3. Create / modify Value

When a Value or Document is created using the default constructor, its type will be Null. To change its type, you need to call SetXXX() or assignment operation, for example:

Document d; // Null
d.SetObject();
 
Value v;    // Null
v.SetInt(10);

//Overload constructor:
Value b(true);              // Call Value(bool)
Value i(-123);              // Call Value(int)
Value u(123u);              // Call Value(unsigned)
Value d(1.5);               // Call Value(double)
//Value(Type)
Value o(kObjectType);       // Call value (empty object)
Value a(kArrayType);        // Call value (empty array)

Move Semantics
When designing RapidJSON, Value assignment does not copy the source Value to the destination Value, but move s the source Value to the destination Value. AddMember(), PushBack() also adopts the transfer semantics. For example:

Value a(123);
Value b(456);
b = a;
// a becomes Null and b becomes the number 123.
// Advantages: improved performance


Value o(kObjectType);
{
    Value contacts(kArrayType);
    // Add elements to the contacts array.
    // ...
    o.AddMember("contacts", contacts, d.GetAllocator());
    // contacts becomes Null here. Its deconstruction is trivial.
}

Transfer semantics - temporary values
Sometimes, you want to construct a Value directly and pass it to a "transfer" function (such as PushBack(), AddMember()). Since temporary objects cannot be converted into normal Value references, a convenient Move() function is added:

Value a(kArrayType);
Document::AllocatorType& allocator = document.GetAllocator();
// a.PushBack(Value(42), allocator);       //  Cannot compile
a.PushBack(Value().SetInt(42), allocator); // fluent API
a.PushBack(Value(42).Move(), allocator);   // Same as the previous line

Create String
RapidJSON provides two String storage policies.

  1. Copy string: allocate the buffer and copy the source data to it.
  2. Const string: simply store a pointer to a string.
//When assigning a copy string
//Call SetString() overloaded function with allocator
Document document;
Value author;
char buffer[10];
int len = sprintf(buffer, "%s %s", "Milo", "Yip");
author.SetString(buffer, len, document.GetAllocator());
memset(buffer, 0, sizeof(buffer));
// After emptying the buffer, author Getstring() still contains "Milo Yip"

For character pointers, RapidJSON needs to be marked to mean that it is safe not to copy. You can use the StringRef function:

const char * cstr = getenv("USER");
size_t cstr_len = ...;                 // If there is a length
Value s;
// s.SetString(cstr);                  //  This cannot be compiled
s.SetString(StringRef(cstr));          // Yes, suppose its life cycle is safe and ends with a null character
s = StringRef(cstr);                   // Uplink abbreviation
s.SetString(StringRef(cstr, cstr_len));// Faster, can handle empty characters
s = StringRef(cstr, cstr_len);         // Uplink abbreviation

A Value of type Array provides an API similar to std::vector. Note that Reserve(...) and PushBack(...) may allocate memory for Array elements, so an allocator is required.

Clear()
Reserve(SizeType, Allocator&)
Value& PushBack(Value&, Allocator&)
template <typename T> GenericValue& PushBack(T, Allocator&)
Value& PopBack()
ValueIterator Erase(ConstValueIterator pos)
ValueIterator Erase(ConstValueIterator first, ConstValueIterator last)

Keywords: JSON

Added by moret on Wed, 22 Dec 2021 08:44:39 +0200