1. rapidjson是什么
rapidjson是腾讯的开源Json解析框架,用C++代码实现,用于解析和生成JSON由于全部代码仅用头文件实现,因此很容易集成在项目中。根据其作者Milo Yipz所做的比较,可以看出rapidjson的性能非常可观。通过使用DOM(Document Object Model)可以很方便的将Json转化成DOM,然后查询修改,再转化为一个Json。通过rapidjson库,可以方便我们进行参数的传递,解析的工作。Json非常便捷的支持键值对、数组、以及深入的嵌套,在编写程序时,可以帮助我们聚焦于业务,而对于参数的传递,则可以较少的投入精力。
1.1 rapidjson的特征。
RapidJSON是一个C++的JSON解析器及生成器。它的灵感来自RapidXml
RapidJSON是一个C++的JSON解析器及生成器。它的灵感来自RapidXml
RapidJSON是一个C++的JSON解析器及生成器。它的灵感来自RapidXml
RapidJSON独立。它不依赖于BOOST等外部库。它甚至不依赖于STL
RapidJSON独立。它不依赖于BOOST等外部库。它甚至不依赖于STL
RapidJSON对Unicode友好。它支持UTF-8、UTF-16、UTF-32 (大端序/小端序),并内部支持这些编码的检测、校验及转码。例如,RapidJSON可以在分析一个UTF-8文件至DOM时,把当中的JSON字符串转码至UTF-16。它也支持代理对(surrogate pair)及”\u0000”(空字符)
这些特征使得我们,可以很好的把rapidjson集成到项目代码之中,提高开发的效率。
1.2 语法规则
在实际使用中,可以把rapidjson把数据组成了一个树形结构,其中,每个节点均为一个Value,Value的类型可以为对象,也可以为一个数组,在Value为对象时,可以是Int,也可以是String,可以是Bool,这些值非常灵活的组织成容易检索获取修改的扩展类型。
1.2.1 数据保存在键值key-value格式组成
在rapidjson中,常见的数据格式为“key”:Value,名称/值,名称为双引号括起来的字符串组成,而值的类型多种多样,可以为整数、浮点数、字符串、也可以为对象,也可以为数组、甚至可以为true、false、null等特殊值在rapidjson官网,给出了一个由C语言组织起来的Json, 类型为const char* json,其取值如下:
可以通过以下的代码把该char* json转换成一个DOM,
#include “rapidjson/document.h”
using namespace rapidjson
//…
Document doc;
doc.parse(json);
1.2.2 数据之间由逗号隔开
在理解时,可以把一个Document理解成键值对的集合,把每一个键值对理解成为Document的一部分。如果写过Python代码,可以很好的理解这种格式。多个键值对用逗号隔开,已表示键值对的结束。
1.2.3 花括号用来保存对象
对象由键值对数组组成,是键值对的集合。在访问对象时,通过检索key而获取value。
1.2.4 方括号用来保存数组
数组保存的元素类型为不具名的对象。可以使用数组下标的方式进行访问,也可以使用迭代器。另外,数组的元素可以是多样的,可以是普通的类型,也可以是对象,或者是嵌套的数组。
2. rapidjson检索
2.1 Document
从字符串经过Parse函数转化而来的类型,Document是一个对象,因此可以使用HasMember之类的成员。Document的类型如下:
class GenericDocument : public GenericValue
bool IsNull() const { return data_.f.flags == kNullFlag; }
bool IsFalse() const { return data_.f.flags == kFalseFlag; }
bool IsTrue() const { return data_.f.flags == kTrueFlag; }
bool IsBool() const { return (data_.f.flags & kBoolFlag) != 0; }
bool IsObject() const { return data_.f.flags == kObjectFlag; }
bool IsArray() const { return data_.f.flags == kArrayFlag; }
bool IsNumber() const { return (data_.f.flags & kNumberFlag) != 0; }
bool IsInt() const { return (data_.f.flags & kIntFlag) != 0; }
bool IsUint() const { return (data_.f.flags & kUintFlag) != 0; }
bool IsInt64() const { return (data_.f.flags & kInt64Flag) != 0; }
bool IsUint64() const { return (data_.f.flags & kUint64Flag) != 0; }
bool IsDouble() const { return (data_.f.flags & kDoubleFlag) != 0; }
bool IsString() const { return (data_.f.flags & kStringFlag) != 0; }
int GetInt() const{ RAPIDJSON_ASSERT(data_.f.flags & kIntFlag); return data_.n.i.i; }
unsigned GetUint() const{ RAPIDJSON_ASSERT(data_.f.flags & kUintFlag); return data_.n.u.u; }
const Ch* GetString() const { RAPIDJSON_ASSERT(IsString()); return (data_.f.flags & kInlineStrFlag) ? data_.ss.str : GetStringPointer(); }
2.4 访问模式
自从RFC 7159 作出更新,合法 JSON 文件的根可以是任何类型的 JSON 值。而在较早的 RFC 4627 中,根值只允许是 Object 或 Array。而在上述例子中,根是一个 Object。欲访问document的“hello”成员,需要先判断具有该成员,并且需要知道该类型为string,如果使用GetInt来获取本为string的“hello”键,发生的行为无法
assert(document.HasMember(“hello”));
assert(document[“hello”].IsString());
printf(“hello = %s\n”,document[“hello”].GetString());
代码首先判断document具有标签“hello”,然后判断该成员对应的值的类型为string,获取则是通过调用document[“hello”].GetString()解析出来该参数。
因此最常见的模式便如下:
i. 判断某个Value具有成员标签HasMember
ii. 判断该成员标签对应的值具有特定的类型IsString,IsInt,IsInt64、IsArray之类。
iii. 获取对应key的值,通过参数GetString,GetInt,GetArray,然后进行业务的处理。
在Json中,使用Number表示数据类型,但在C++程序处理时,往往需要具体的类型。查看下面的代码片段:
assert(document["i"].IsNumber());
// 在此情况下,IsUint()/IsInt64()/IsUInt64() 也会返回 true
assert(document["i"].IsInt());
printf("i = %d\n",document["i"].GetInt());
// 另一种用法: (int)document["i"]
assert(document["pi"].IsNumber());
assert(document["pi"].IsDouble());
printf("pi = %g\n",document["pi"].GetDouble());
整型数据、浮点型数据在判断IsNumber均返回真值,在获取特定类型比如Int,Int64,Double则必须使用相应的Get方法才可,通过强转也可以达到相同的目的,即(int)document[“i”];
2.5 查询Array
在document对象的标签“a”对应一个数组,我们可以通过下面的代码片段查看json如何检索数组元素。缺省情况下,SizeType 是 unsigned 的 typedef。在多数系统中,Array 最多能存储 2^32-1 个元素。
你可以用整数字面量访问元素,如 a[0]、a[1]、a[2]。
// 使用引用来连续访问,方便之余还更高效。
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());
上面的代码使用索引访问,但在之前我们通过引用获取了document中标签为“a“的值,使用Value类型承接。通过断言确保a是数组同样也可以使用迭代器进行访问元素,如下:
for (Value::ConstValueIterator itr = a.Begin(); itr != a.End(); ++itr)
printf(“%d “,itr->GetInt());
可以在上两段代码片段中看到,a.size()返回了数组中元素的个数,a.Begin()/a.End()返回了指向第一个元素的迭代器,和指向最后一个元素之后的迭代器。另外可以通过方法a.Empty(),a.Capacity()返回数组是否为空数组和容量。
2.6 查询Object
//! Type of JSON value
enum Type {
kNullType = 0,//!< null
kFalseType = 1,//!< false
kTrueType = 2,//!< true
kObjectType = 3,//!< object
kArrayType = 4,//!< array
kStringType = 5,//!< string
kNumberType = 6 //!< number
};
在官网教程中,我们看到了下面的例子,即通过迭代器访问所有的Object成员:代码片段如下
static const char* kTypeNames[] =
{ "Null","False","True","Object","Array","String","Number" };
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
若我们不确定一个成员是否存在,便需要在调用 operator[](const char*) 前先调用 HasMember()。然而,这会导致两次查找。更好的做法是调用 FindMember(),它能同时检查成员是否存在并返回它的 Value:
Value::ConstMemberIterator itr = document.FindMember("hello");
if (itr != document.MemberEnd())
printf("%s\n",itr->value.GetString());
2.7 查询Number
JSON 只提供一种数值类型──Number。数字可以是整数或实数。RFC 4627 规定数字的范围由解析器指定。
由于 C++ 提供多种整数及浮点数类型,DOM 尝试尽量提供最广的范围及良好性能。
当解析一个 Number 时,它会被存储在 DOM 之中,成为下列其中一个类型:
类型 描述
unsigned 32 位无号整数
int 32 位有号整数
uint64_t 64 位无号整数
int64_t 64 位有号整数
double 64 位双精度浮点数
当查询一个 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()
注意,一个整数可能用几种类型来提取,而无需转换。例如,一个名为 x 的 Value 包含 123,那么 x.IsInt() == x.IsUint() == x.IsInt64() == x.IsUint64() == true。但如果一个名为 y 的 Value 包含 -3000000000,那么仅会令 x.IsInt64() == true。
当要提取 Number 类型,GetDouble() 是会把内部整数的表示转换成 double。注意 int 和 unsigned 可以安全地转换至 double,但 int64_t 及 uint64_t 可能会丧失精度(因为 double 的尾数只有 52 位)。
2.8 比较两个Value
你可使用 == 及 != 去比较两个 Value。当且仅当两个 Value 的类型及内容相同,它们才当作相等。你也可以比较 Value 和它的原生类型值。以下是一个例子。
if (document["hello"] == document["n"]) /*...*/; // 比较两个值
if (document["hello"] == "world") /*...*/; // 与字符串家面量作比较
if (document["i"] != 123) /*...*/; // 与整数作比较
if (document["pi"] != 3.14) /*...*/; // 与 double 作比较
Array/Object 顺序以它们的元素/成员作比较。当且仅当它们的整个子树相等,它们才当作相等。
3. 创建/修改值
有多种方法去创建值。 当一个 DOM 树被创建或修改后,可使用 Writer 再次存储为 JSON。
当使用默认构造函数创建一个 Value 或 Document,它的类型便会是 Null。要改变其类型,需调用 SetXXX() 或赋值操作,例如:
Document d; // Null
d.SetObject();
Value v; // Null
v.SetInt(10);
v = 10; // 简写,和上面的相同
3.1 构造函数的各个重载
几个类型也有重载构造函数:
Value b(true); // 调用 Value(bool)
Value i(-123); // 调用 Value(int)
Value u(123u); // 调用 Value(unsigned)
Value d(1.5); // 调用 Value(double)
要重建空 Object 或 Array,可在默认构造函数后用 SetObject()/SetArray(),或一次性使用 Value(Type):
Value o(kObjectType);
Value a(kArrayType);
3.2 转移语义[Move Semantics]
Value复制指的是在两个Value进行复制时不是传统意义上的把来源 Value 复制至目的 Value,而是把把来源 Value 转移(move)至目的 Value。
Value a(123);
Value b(456);
b = a; // a 变成 Null,b 变成数字 123。
3.3 创建String
RapidJSON 提供两个 String 的存储策略。
1. copy-string: 分配缓冲区,然后把来源数据复制至它。
2. const-string: 简单地储存字符串的指针。
Copy-string 总是安全的,因为它拥有数据的克隆。Const-string 可用于存储字符串字面量,以及用于在 DOM 一节中将会提到的 in-situ 解析中。
为了让用户自定义内存分配方式,当一个操作可能需要内存分配时,RapidJSON 要求用户传递一个 allocator 实例作为 API 参数。此设计避免了在每个 Value 存储 allocator(或 document)的指针。
当我们把一个 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"
最后,对于字符串字面量或有安全生命周期的字符串,可以使用 const-string 版本的 SetString(),它没有 allocator 参数。对于字符串家面量(或字符数组常量),只需简单地传递字面量,又安全又高效:
Value s;
s.SetString("rapidjson"); // 可包含空字符,长度在编译萁推导
s = "rapidjson"; // 上行的缩写
可以通过C++的std::string结构创建rapidjson的String结构,可以通过这种方式创建rapidjson的字符串,代码中同时演示了吧Value转换成std::string的方法,代码示例如下:
std::list<DeviceIdPannelInfo>::const_iterator iter = Remotes.begin();
*Document document;
Document::AllocatorType& allocator = document.GetAllocator();
Value StateInfos(kArrayType);*
for (; iter != Remotes.end(); iter++)
{
int DevState;
std::string DeviceIdToSearch(iter->deviceId);
int DevicePanelId = iter->panelId;
*Value state(kObjectType);*
int sdkRet = HikTalk_GetDevStatus(const_cast<char*>(DeviceIdToSearch.c_str()),DevicePanelId,&DevState,const_cast<char*>(m_ServerId.c_str()),const_cast<char*>(m_ServerIp.c_str()),m_ServerPort);
if (sdkRet != HIKTALK_E_OK)
{
TALKCLIENTPLUGIN_ERROR("- Get Device state fail,the deviceId is %s,the errCode is %d",DeviceIdToSearch.c_str(),sdkRet);
Msg = "Get Device State Fail";
return DEV_ERR_Failed;
}else
{
Value TempId;
*TempId.SetString(DeviceIdToSearch.c_str(),DeviceIdToSearch.length(),allocator);*
*state.AddMember(TempId,DevState,allocator);*
*StateInfos.PushBack(state,allocator);*
}
}
rapidjson::StringBuffer sbBuffer;
rapidjson::Writer<rapidjson::StringBuffer> jWriter(sbBuffer);
StateInfos.Accept(jWriter);
TALKCLIENTPLUGIN_TRACE("- CHikIntercomDevice::GetDeviceState ends");
Msg = std::string(sbBuffer.GetString());
TALKCLIENTPLUGIN_DEBUG("- Msg: %s",Msg.c_str());
return DEV_ERR_SUCCESS;
上面的代码中演示了如果通过C++标准库std::string创建rapidjson的对象的过程。关键语句为
TempId.SetString(DeviceIdToSearch.c_str(),allocator);
在工作中实践rapidjson的使用,按照自己的目的来输入和输出是最紧要的事情。
3.4 修改Array
Array 类型的 Value 提供与 std::vector 相似的 API。
• 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)
注意,Reserve(…) 及 PushBack(…) 可能会为数组元素分配内存,所以需要一个 allocator。一个简单示例:
Value a(kArrayType);
Document::AllocatorType& allocator = document.GetAllocator();
for (int i = 5; i <= 10; i++)
a.PushBack(i,allocator); // 可能需要调用 realloc() 所以需要 allocator
// 流畅接口(Fluent interface)
a.PushBack("Lua",allocator).PushBack("Mio",allocator);
PushBack()/PopBack() 返回 Array本身的引用。这称为流畅接口(_fluent interface_)。
Value v(kArrayType);
Value v1(kObjectType);
v1.AddMember("1","2",d.GetAllocator());
v.PushBack(1,d.GetAllocator()).PushBack("1",d.GetAllocator()).PushBack(v1,d.GetAllocator());
std::string str = JsonToString(v);
输出的结果如下:
{“project”:”rapidjson”,”stars”:11}
[1,”1”,{“1”:”2”}]
上面的代码片段表明,可以在一个在一个类型为数组的Value中添加任意类型的元素。
3.5 修改Object
Object 是键值对的集合。每个键必须为 String。要修改 Object,方法是增加或移除成员。对于集合的操作包括添加成员,删除成员,修改成员。
• Value& AddMember(StringRefType,Value&,Allocator&)template <typename T>
• Value& AddMember(StringRefType,T value,Allocator&)
• Value& AddMember(Value&,Allocator& allocator)
上述的方法为向一个类型为kObjectType的Value添加键值对的方法。
Value contact(kObject);
contact.AddMember(“name”,“Milo”,document.GetAllocator());
contact.AddMember(“married”,true,document.GetAllocator());
如果你需要从非常数字符串或生命周期不足的字符串创建键名(见 创建 String),你需要使用 copy-string API。为了避免中间变量,可以就地使用 临时值:
// 就地 Value 参数
contact.AddMember(Value("copy",document.GetAllocator()).Move(),// copy string
Value().Move(),// null value
document.GetAllocator());
// 显式参数
Value key("key",document.GetAllocator()); // copy string name
Value val(42); // 某 Value
contact.AddMember(key,val,document.GetAllocator());
• bool RemoveMember(const Ch* name):使用键名来移除成员(线性时间复杂度)。
• bool RemoveMember(const Value& name):除了 name 是一个 Value,和上一行相同。
• MemberIterator RemoveMember(MemberIterator):使用迭代器移除成员(_ 常数 _ 时间复杂度)。
• MemberIterator EraseMember(MemberIterator):和上行相似但维持成员次序(线性时间复杂度)。
• MemberIterator EraseMember(MemberIterator first,MemberIterator last):移除一个范围内的成员,维持次序(线性时间复杂度)。
3.6 深拷贝
若我们真的要复制一个 DOM 树,我们可使用两个 APIs 作深复制:含 allocator 的构造函数及 CopyFrom():
Document d;
Document::AllocatorType& a = d.GetAllocator();
Value v1("foo");
// Value v2(v1); // 不容许
Value v2(v1,a); // 制造一个克隆
assert(v1.IsString()); // v1 不变
d.SetArray().PushBack(v1,a).PushBack(v2,a);
assert(v1.IsNull() && v2.IsNull()); // 两个都转移动 d
v2.CopyFrom(d,a); // 把整个 document 复制至 v2
assert(d.IsArray() && d.Size() == 2); // d 不变
v1.SetObject().AddMember("array",v2,a);
d.PushBack(v1,a);
3.7 交换Value
3.8 解析json然后重新生成json字符串
3.7.1 Document转化为json字符串
之前主要是这个过程看不太明白,因此在网上看到了如下的代码片段,通过Writer将Value转换成json合适的数据格式。而Reader则是把json字符串转换成Value。
/* 下载rapidjson-master.zip,解压后,把include文件夹拷贝到自己的工程中就可以使用了 只需要include,不需要加载.lib,.dll */
// rapidjson/example/simpledom/simpledom.cpp`
#include "rapidjson/document.h"
#include "rapidjson/writer.h"
#include "rapidjson/stringbuffer.h"
#include <iostream>
using namespace rapidjson;
int main() {
// 1. 把 JSON 解析至 DOM。
const char* json = "{\"project\":\"rapidjson\",\"stars\":10}";
Document d;///< 创建一个Document对象 rapidJson的相关操作都在Document类中
d.Parse(json);///< 通过Parse方法将Json数据解析出来
if (!d.HasParseError())
{
//这里要注意一点就是一定要对解析出来的document(JSON解析出来以xml dom形式存在)进行判断,判断是否解析正确,否则后面一切处理均是无效的。
// 2. 利用 DOM 作出修改。
Value& s = d["stars"];
/* Value:value其实就是var,对于value可以理解为int,也是理解为string,或者是bool型变量等其他数据类型。对于定义Value value,只是一个定义,还没有决定其数据类型,如果明确value的值,则相应确定其数据类型了。 Json数据类型是一个map,表示为key-value形式,对于Value转换为基础数据类型有 一下几种方法: vall.SetArray() vall.SetArrayRaw() vall.SetBool() vall.SetDouble() vall.SetInt() vall.SetNull() vall.SetObject() vall.SetString() vall.SetStringRaw() vall.SetUint(); 同时,对于value的数据类型,是可以重复设置。 */
s.SetInt(s.GetInt() + 1);
// 3. 把 DOM 转换(stringify)成 JSON。
StringBuffer buffer;
Writer<StringBuffer> writer(buffer);
d.Accept(writer);
// Output {"project":"rapidjson","stars":11}
std::cout << buffer.GetString() << std::endl;
}
return 0;
}
上述的代码片段通过StringBuffer作为桥梁,把Document的变量d转化到缓冲数组buffer中,此时输出结果在代码里有显示。这是具体的过程。
3.7.2 Value转化为json字符串
直接通过Value转化为json字符串的代码片段如下
#include "rapidjson/document.h"
#include "rapidjson/writer.h"
#include "rapidjson/stringbuffer.h"
string strJsonTest = "{\"item_1\":{\"sub_item_1\":\"value_1\",\"sub_item_2\":\"value_2\",\"sub_item_3\":\"value_3\"},\"item_2\":\"value_2\"}";
int main(void) {
Document docTest;
docTest.Parse<0>(strJsonTest.c_str());
if (!docTest.HasParseError())
{
if (docTest.HasMember("item_1"))
{
rapidjson::Value& valObj = docTest["item_1"];
rapidjson::StringBuffer sbBuf;
rapidjson::Writer<rapidjson::StringBuffer> jWriter(sbBuf);
valObj.Accept(jWriter);
std::string strTemp = std::string(sbBuf.GetString());
//strTemp的内容为 {\"sub_item_1\":\"value_1\",\"sub_item_2\":\"value_2\",\"sub_item_3\":\"value_3\"}
}
}
把该过程封装函数如下:
std::string JsonToString(const rapidjson::Value& valObj)
{
rapidjson::StringBuffer sbBuf;
rapidjson::Writer<rapidjson::StringBuffer> jWriter(sbBuf);
valObj.Accept(jWriter);
return std::string(sbBuf.GetString());
}
4. 代码片段–实践出真知
4.1 rapidjson的Value的组成和Value到std::string的转换
下述代码片段描述了使用rapidjson所要包含的常用头文件和基本使用。演示了从Value到String对象的转换,并且也简单演示了Value取不同类型的赋值,即构造Json串的过程。
#include <stdio.h>
#include <stdio.h>
#include <string>
#include <iostream>
#include "rapidjson/document.h"
#include "rapidjson/prettywriter.h"
#include "rapidjson/filestream.h"
#include "rapidjson/stringbuffer.h"
using namespace std;
using namespace rapidjson;
int main(int argc,char *argv[])
{
printf("Lu//a\"\n");
Document document;
Document::AllocatorType& allocator = document.GetAllocator();
Value contact(kArrayType);
Value contact2(kArrayType);
Value root(kArrayType);
//构造了两个个kArrayType类型的Value,因为数组元素的追加包括了内存空间的分配,因此需要具有分配器。分配器获得的方式参见标蓝位置
contact.PushBack("Lu//a\"",allocator).PushBack("Mio",allocator).PushBack("",allocator);
contact2.PushBack("Lu// a",allocator);
//root本身也是一个kArrayType,而且其两个元素的类型为kArrayType
root.PushBack(contact,allocator);
root.PushBack(contact2,allocator);
//上述构造的rapidjson数据结构的根为Value,其类型为kArrayType,从Value转换成std::string的过程如下:此过程之前我搞不清楚,花了许多时间
StringBuffer buffer;
Writer<StringBuffer> writer(buffer);
root.Accept(writer);
string reststring = buffer.GetString();
cout << reststring << endl;
return 0;
}
输出结果如下所示:
Lu//a" [["Lu//a\"","Mio",""],["Lu// a",""]]
对象JSON
下述代码片段包含的重要内容就是如何通过std::string类型的对象创建一个含有相应值得Value(kStringType)对象,并且添加到Value(kObjectType)对象中的演示。
void TestJson2()
{
Document document;
//获得分配器
Document::AllocatorType& allocator = document.GetAllocator();
//root为kObjectType
Value root(kObjectType);
//storage_photo_count为Value的String类型,注意定义的格式
Value storage_photo_count(kStringType);
//使用std::string类型的构造方式创建了一个std::string值
std::string storage_photo_count_str("49");
//通过Value的SetString方法把一个std::string类型对象的内容赋值给了rapidjson的String对象
storage_photo_count.SetString(storage_photo_count_str.c_str(),storage_photo_count_str.size(),allocator);
Value storage_music_count(kStringType);
std::string storage_music_count_str("48");
storage_music_count.SetString(storage_music_count_str.c_str(),storage_music_count_str.size(),allocator);
//root为对象,添加成员的方式具有allocator,一个字符串字面值,一个Value(kStringType)
root.AddMember("storage.photo.count",storage_photo_count,allocator);
root.AddMember("storage.music.count",storage_music_count,allocator);
StringBuffer buffer;
Writer<StringBuffer> writer(buffer);
root.Accept(writer);
std::string result = buffer.GetString();
cout << "result: " << result << "..........:" << result.size()<< endl;
}
4.3 在Value(kObjectType)添加各种类型的值
/// 添加一个String对象;
rapidjson::Document::AllocatorType&allocator = doc.GetAllocator(); ///< 获取最初数据的分配器
rapidjson::Value strObject(rapidjson::kStringType); ///<添加字符串方法1
strObject.SetString("love");
doc.AddMember("hello1",strObject,allocator);
/*doc.AddMember("hello1","love you",allocator); ///<添加字符串方法2:往分配器中添加一个对象*/
/// 添加一个null对象
rapidjson::Value nullObject(rapidjson::kNullType);
doc.AddMember("null",nullObject,allocator); ///<往分配器中添加一个对象
/// 添加一个数组对象
rapidjson::Value array(rapidjson::kArrayType); ///< 创建一个数组对象
rapidjson::Value object(rapidjson::kObjectType); ///<创建数组里面对象。
object.AddMember("id",1,allocator);
//普通常用的用法中,键为字符串字面值,而值得类型可以为字符串字面值,true/false
object.AddMember("name","lai",allocator);
object.AddMember("age","12",allocator);
object.AddMember("low",true,allocator);
array.PushBack(object,allocator);
doc.AddMember("player",array,allocator); ///<将上述的数组内容添加到一个名组中
/// 在已有的数组中添加一个成员对象
rapidjson::Value& aArray1 = doc["a"]; aArray1.PushBack(2.0,allocator);
GitLab–综合例子
下面的例子几乎涵盖了rapidjson常用的各个方面,请在实践中体会。
// Hello World example
// This example shows basic usage of DOM-style API.
#include "rapidjson/document.h" // rapidjson's DOM-style API
#include "rapidjson/prettywriter.h" // for stringify JSON
#include <cstdio>
using namespace rapidjson;
using namespace std;
int main(int,char*[]) {
////////////////////////////////////////////////////////////////////////////
// 1. Parse a JSON text string to a document.
const char json[] = " { \"hello\" : \"world\",\"t\" : true,\"f\" : false,\"n\": null,\"i\":123,\"pi\": 3.1416,\"a\":[1,2,3,4] } ";
printf("Original JSON:\n %s\n",json);
Document document; // Default template parameter uses UTF8 and MemoryPoolAllocator.
#if 0
// "normal" parsing,decode strings to new buffers. Can use other input stream via ParseStream().
//从char*转化为document
if (document.Parse(json).HasParseError())
return 1;
#else
// In-situ parsing,decode strings directly in the source string. Source must be string.
char buffer[sizeof(json)];
memcpy(buffer,json,sizeof(json));
if (document.ParseInsitu(buffer).HasParseError())
return 1;
#endif
printf("\nParsing to document succeeded.\n");
////////////////////////////////////////////////////////////////////////////
// 2. Access values in document.
printf("\nAccess values in document:\n");
assert(document.IsObject()); // Document is a JSON value represents the root of DOM. Root can be either an object or array.
//使用断言确保解析过后的document含有成员hello,并且是字符串
assert(document.HasMember("hello"));
assert(document["hello"].IsString());
printf("hello = %s\n",document["hello"].GetString());
// Since version 0.2,you can use single lookup to check the existing of member and its value:
//document为kObjectType,可以通过FindMember返回Value::MbmberIterator迭代器
Value::MemberIterator hello = document.FindMember("hello");
assert(hello != document.MemberEnd());
assert(hello->value.IsString());
//含字符串比较
assert(strcmp("world",hello->value.GetString()) == 0);
(void)hello;
assert(document["t"].IsBool()); // JSON true/false are bool. Can also uses more specific function IsTrue().
printf("t = %s\n",document["t"].GetBool() ? "true" : "false");
assert(document["f"].IsBool());
printf("f = %s\n",document["f"].GetBool() ? "true" : "false");
printf("n = %s\n",document["n"].IsNull() ? "null" : "?");
assert(document["i"].IsNumber()); // Number is a JSON type,but C++ needs more specific type.
assert(document["i"].IsInt()); // In this case,IsUint()/IsInt64()/IsUInt64() also return true.
printf("i = %d\n",document["i"].GetInt()); // Alternative (int)document["i"]
assert(document["pi"].IsNumber());
assert(document["pi"].IsDouble());
printf("pi = %g\n",document["pi"].GetDouble());
{
const Value& a = document["a"]; // Using a reference for consecutive access is handy and faster.
assert(a.IsArray());
for (SizeType i = 0; i < a.Size(); i++) // rapidjson uses SizeType instead of size_t.
printf("a[%d] = %d\n",a[i].GetInt());
int y = a[0].GetInt();
(void)y;
// Iterating array with iterators
printf("a = ");
for (Value::ConstValueIterator itr = a.Begin(); itr != a.End(); ++itr)
printf("%d ",itr->GetInt());
printf("\n");
}
// Iterating object members
static const char* kTypeNames[] = { "Null","False","True","Object","Array","String","Number" };
for (Value::ConstMemberIterator itr = document.MemberBegin(); itr != document.MemberEnd(); ++itr)
printf("Type of member %s is %s\n",kTypeNames[itr->value.GetType()]);
////////////////////////////////////////////////////////////////////////////
// 3. Modify values in document.
// Change i to a bigger number
{
uint64_t f20 = 1; // compute factorial of 20
for (uint64_t j = 1; j <= 20; j++)
f20 *= j;
document["i"] = f20; // Alternate form: document["i"].SetUint64(f20)
assert(!document["i"].IsInt()); // No longer can be cast as int or uint.
}
// Adding values to array.
{
Value& a = document["a"]; // This time we uses non-const reference.
Document::AllocatorType& allocator = document.GetAllocator();
for (int i = 5; i <= 10; i++)
a.PushBack(i,allocator); // May look a bit strange,allocator is needed for potentially realloc. We normally uses the document's.
// Fluent API
a.PushBack("Lua",allocator);
}
// Making string values.
//构造Value兼容的String对象
// This version of SetString() just store the pointer to the string.
// So it is for literal and string that exists within value's life-cycle.
{
document["hello"] = "rapidjson"; // This will invoke strlen()
// Faster version:
// document["hello"].SetString("rapidjson",9);
}
// This version of SetString() needs an allocator,which means it will allocate a new buffer and copy the the string into the buffer.
Value author;
{
char buffer2[10];
//组装成char*,通过传入字符串指针,和长度创建Value兼容的String对象
int len = sprintf(buffer2,"Yip"); // synthetic example of dynamically created string.
author.SetString(buffer2,static_cast<SizeType>(len),document.GetAllocator());
// Shorter but slower version:
// document["hello"].SetString(buffer,document.GetAllocator());
// Constructor version:
// Value author(buffer,document.GetAllocator());
// Value author(buffer,document.GetAllocator());
memset(buffer2,sizeof(buffer2)); // For demonstration purpose.
}
// Variable 'buffer' is unusable now but 'author' has already made a copy.
document.AddMember("author",author,document.GetAllocator());
assert(author.IsNull()); // Move semantic for assignment. After this variable is assigned as a member,the variable becomes null.
////////////////////////////////////////////////////////////////////////////
// 4. Stringify JSON,把JSON对象std::string化
printf("\nModified JSON with reformatting:\n");
StringBuffer sb;
PrettyWriter<StringBuffer> writer(sb);
document.Accept(writer); // Accept() traverses the DOM and generates Handler events.
puts(sb.GetString());
return 0;
}
5. 引用
原文链接:https://www.f2er.com/json/288573.htmlhttps://www.cnblogs.com/549294286/p/6067763.html
http://rapidjson.org/md_doc_tutorial.html#ValueDocument