RapidJSON优点
-
跨平台
编译器:Visual Studio、gcc、clang 等
架构:x86、x64、ARM 等
操作系统:Windows、Mac OS X、Linux、iOS、Android 等 -
容易安装
只有头文件的库。只需把头文件复制至你的项目中。 -
独立、最小依赖
不需依赖 STL、BOOST 等。
只包含 <cstdio>, <cstdlib>, <cstring>, <inttypes.h>, <new>, <stdint.h>。 -
没使用 C++ 异常、RTTI
-
高性能
使用模版及内联函数去降低函数调用开销。
内部经优化的 Grisu2 及浮点数解析实现。
可选的 SSE2/SSE4.2 支持。
RapidJSON教程
1、Value 及 Document
每个 JSON 值都储存为 Value 类,一个 Value 类可包含不同类型的值,而 Document 类则表示整个 DOM,它存储了一个 DOM 树的根 Value。
定义一个JSON字符串,把它解析至一个 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()
//在接收服务器数据时一般是字符串。
//可以使用 JSON.parse() 将数据转换为 JavaScript 对象。
那么现在该 JSON 就会被解析至 document 中,成为一棵DOM 树:
2、查询Value
//根是一个 Object,验证它的类型
assert(document.IsObject());//查询一下根 Object 中有没有 "hello" 成员
//在此例中,"hello" 成员关联到一个 JSON String
assert(document.HasMember("hello"));
assert(document["hello"].IsString());
printf("hello = %s\n", document["hello"].GetString());
//打印结果:world//JSON True/False 值是以 bool 表示的。
assert(document["t"].IsBool());
printf("t = %s\n", document["t"].GetBool() ? "true" : "false");
//打印结果:true//JSON Null 值可用 IsNull() 查询。
printf("n = %s\n", document["n"].IsNull() ? "null" : "?");
//打印结果:null//JSON Number 类型表示所有数值
//然而,C++ 需要使用更专门的类型
assert(document["i"].IsNumber());
assert(document["i"].IsInt());// 在此情况下,IsUint()/IsInt64()/IsUint64() 也会返回 true
printf("i = %d\n", document["i"].GetInt());
//打印结果:i = 123
assert(document["pi"].IsNumber());
assert(document["pi"].IsDouble());
printf("pi = %g\n", document["pi"].GetDouble());
//打印结果:pi = 3.1416// 使用引用来连续访问,方便之余还更高效。
const Value& a = document["a"];
assert(a.IsArray());
for (SizeType i = 0; i < a.Size(); i++) // 使用 SizeType 而不是 size_tprintf("a[%d] = %d\n", i, a[i].GetInt());
//打印结果:a[0] = 1
// a[1] = 2
// a[2] = 3
// a[3] = 4//用迭代器去访问所有 Object 成员:
static const char* kTypeNames[] =
{"Null", "False", "True", "Object", "Array", "String", "Number"
};//若我们不确定一个成员是否存在,便需要在调用
//1、HasMember()
//2、operator[](const char*)
//然而,这会导致两次查找
//更好的做法是调用 FindMember()
//它能同时检查成员是否存在并返回它的 Value
Value::ConstMemberIterator itr = document.FindMember("hello");
if (itr != document.MemberEnd())printf("%s\n", itr->value.GetString());
//打印结果:worldfor (Value::ConstMemberIterator itr = document.MemberBegin();itr != document.MemberEnd(); ++itr)
{printf("Type of member %s is %s\n",itr->name.GetString(), kTypeNames[itr->value.GetType()]);
}
//打印结果: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//当使用 C++11 功能时,你可使用范围 for 循环去访问 Object 内的所有成员。
for (auto& m : document.GetObject())printf("Type of member %s is %s\n",m.name.GetString(), kTypeNames[m.value.GetType()]);
//打印结果: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//比较两个 Value
//使用 == 及 != 去比较两个 Value
//当且仅当 两个 Value 的类型及内容相同,它们才当作相等
if (document["hello"] == document["n"]) /*...*/; // 比较两个值
if (document["hello"] == "world") /*...*/; // 与字符串字面量作比较
if (document["i"] != 123) /*...*/; // 与整数作比较
if (document["pi"] != 3.14) /*...*/; // 与 double 作比较
JSON 只提供一种数值类型──Number。数字可以是整数或实数,而 C++ 提供多种整数及浮点数类型 当查询一个 Number 时,你可以检查该数字是否能以目标类型来提取:
查检 | 提取 |
---|---|
bool IsNumber() | 不适用 |
bool IsUint() | unsigned GetUint() |
bool IsInt() | int GetInt() |
bool IsUint64() | uint64_t GetUint64() |
bool IsInt64() | int64_t GetInt64() |
bool IsDouble() | double GetDouble() |
3、创建/修改Value
当使用默认构造函数创建一个 Value 或 Document,它的类型便会是 Null。要改变其类型,需调用 SetXXX() 或赋值操作,例如:
Document d; // Null
d.SetObject();Value v; // Null
v.SetInt(10);//重载构造函数:
Value b(true); // 调用 Value(bool)
Value i(-123); // 调用 Value(int)
Value u(123u); // 调用 Value(unsigned)
Value d(1.5); // 调用 Value(double)
//Value(Type)
Value o(kObjectType); // 调用 Value(空object)
Value a(kArrayType); // 调用 Value(空array)
转移语义(Move Semantics)
在设计 RapidJSON 时,Value 赋值并不是把来源 Value 复制至目的 Value,而是把来源 Value 转移(move)至目的 Value,AddMember(), PushBack() 也采用转移语义。例如:
Value a(123);
Value b(456);
b = a;
// a 变成 Null,b 变成数字 123。
// 优点:提高性能Value o(kObjectType);
{Value contacts(kArrayType);// 把元素加进 contacts 数组。// ...o.AddMember("contacts", contacts, d.GetAllocator());// contacts 在这里变成 Null。它的析构是平凡的。
}
转移语义——临时值
有时候,想直接构造一个 Value 并传递给一个“转移”函数(如 PushBack()、AddMember())。由于临时对象是不能转换为正常的 Value 引用,加入了一个方便的 Move() 函数:
Value a(kArrayType);
Document::AllocatorType& allocator = document.GetAllocator();
// a.PushBack(Value(42), allocator); // 不能通过编译
a.PushBack(Value().SetInt(42), allocator); // fluent API
a.PushBack(Value(42).Move(), allocator); // 和上一行相同
创建 String
RapidJSON 提供两个 String 的存储策略。
- copy-string: 分配缓冲区,然后把来源数据复制至它。
- const-string: 简单地储存字符串的指针。
//把一个 copy-string 赋值时
//调用含有 allocator 的 SetString() 重载函数
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));
// 清空 buffer 后 author.GetString() 仍然包含 "Milo Yip"
对于字符指针,RapidJSON 需要作一个标记,代表它不复制也是安全的。可以使用 StringRef 函数:
const char * cstr = getenv("USER");
size_t cstr_len = ...; // 如果有长度
Value s;
// s.SetString(cstr); // 这不能通过编译
s.SetString(StringRef(cstr)); // 可以,假设它的生命周期安全,并且是以空字符结尾的
s = StringRef(cstr); // 上行的缩写
s.SetString(StringRef(cstr, cstr_len));// 更快,可处理空字符
s = StringRef(cstr, cstr_len); // 上行的缩写
Array 类型的 Value 提供与 std::vector 相似的 API。注意,Reserve(…) 及 PushBack(…) 可能会为数组元素分配内存,所以需要一个 allocator。
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)
4、使用字符串缓冲器生成——writer
#include "rapidjson/stringbuffer.h"
#include "rapidjson/writer.h"
#include <iostream>
#include <string>using namespace std;void Serialize_1()
{rapidjson::StringBuffer strBuf;rapidjson::Writer<rapidjson::StringBuffer> writer(strBuf);writer.StartObject();//1. 整数类型writer.Key("Int");writer.Int(1);//2. 浮点类型writer.Key("Double");writer.Double(12.0000001);//3. 字符串类型writer.Key("String");writer.String("This is a string");//4. 结构体类型writer.Key("Object");writer.StartObject();writer.Key("name");writer.String("qq849635649");writer.Key("age");writer.Int(25);writer.EndObject();//5. 数组类型//5.1 整型数组writer.Key("IntArray");writer.StartArray();//顺序写入即可writer.Int(10);writer.Int(20);writer.Int(30);writer.EndArray();//5.2 浮点型数组writer.Key("DoubleArray");writer.StartArray();for(int i = 1; i < 4; i++){writer.Double(i * 1.0);}writer.EndArray();//5.3 字符串数组writer.Key("StringArray");writer.StartArray();writer.String("one");writer.String("two");writer.String("three");writer.EndArray();//5.4 混合型数组//这说明了,一个json数组内容是不限制类型的writer.Key("MixedArray");writer.StartArray();writer.String("one");writer.Int(50);writer.Bool(false);writer.Double(12.005);writer.EndArray();//5.5 结构体数组writer.Key("People");writer.StartArray();for(int i = 0; i < 3; i++){writer.StartObject();writer.Key("name");writer.String("qq849635649");writer.Key("age");writer.Int(i * 10);writer.Key("sex");writer.Bool((i % 2) == 0);writer.EndObject();}writer.EndArray();writer.EndObject();string data = strBuf.GetString();cout << data << endl;
}
#include "rapidjson/document.h"
#include "rapidjson/stringbuffer.h"
#include "rapidjson/writer.h"void Serialize_2()
{rapidjson::Document doc;doc.SetObject();rapidjson::Document::AllocatorType& allocator = doc.GetAllocator();//1. 整型类型doc.AddMember("Int", 1, allocator);//2. 浮点类型doc.AddMember("Double", 12.00001, allocator);//3. 字符串类型//正确方式string str= "This is a string";rapidjson::Value str_value(rapidjson::kStringType);str_value.SetString(str.c_str(), str.size());if(!str_value.IsNull()){doc.AddMember("String", str_value, allocator);}/*** 注:以下方式不正确,可能成功,也可能失败,因为字符串写入json要重新开辟内存,* 如果使用该方式的话,当数据是字符串常量的话是没问题的,如果为变量就会显示乱码,所* 以为保险起见,我们显式的分配内存(无需释放)*///doc.AddMember("String", str.data(), allocator);//4. 结构体类型rapidjson::Value object(rapidjson::kObjectType);object.AddMember("name", "qq849635649", allocator); //注:常量是没有问题的object.AddMember("age", 25, allocator);doc.AddMember("Object", object, allocator);//5. 数组类型//5.1 整型数组rapidjson::Value IntArray(rapidjson::kArrayType);IntArray.PushBack(10, allocator);IntArray.PushBack(20, allocator);IntArray.PushBack(30, allocator);doc.AddMember("IntArray", IntArray, allocator);//5.2 浮点型数组rapidjson::Value DoubleArray(rapidjson::kArrayType);DoubleArray.PushBack(1.0, allocator);DoubleArray.PushBack(2.0, allocator);DoubleArray.PushBack(3.0, allocator);doc.AddMember("DoubleArray", DoubleArray, allocator);//5.3 字符型数组rapidjson::Value StringArray(rapidjson::kArrayType);string strValue1 = "one";string strValue2 = "two";string strValue3 = "three";str_value.SetString(strValue1.c_str(), strValue1.size());StringArray.PushBack(str_value, allocator);str_value.SetString(strValue2.c_str(), strValue2.size());StringArray.PushBack(str_value, allocator);str_value.SetString(strValue3.c_str(), strValue3.size());StringArray.PushBack(str_value, allocator);doc.AddMember("StringArray", StringArray, allocator);//5.4 结构体数组rapidjson::Value ObjectArray(rapidjson::kArrayType);for(int i = 1; i < 4; i++){rapidjson::Value obj(rapidjson::kObjectType);obj.AddMember("name", "qq849635649", allocator);//注:常量是没有问题的obj.AddMember("age", i * 10, allocator);ObjectArray.PushBack(obj, allocator);}doc.AddMember("ObjectArray", ObjectArray, allocator);rapidjson::StringBuffer strBuf;rapidjson::Writer<rapidjson::StringBuffer> writer(strBuf);doc.Accept(writer);string data = strBuf.GetString();cout << data << endl;
}