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_t
printf("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());
//打印结果: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()]);
}
//打印结果: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;
}