rapidjson总结----string和rapijson的互相转换

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.cnblogs.com/549294286/p/6067763.html
http://rapidjson.org/md_doc_tutorial.html#ValueDocument

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


AJAX是一种基于JavaScript和XML的技术,能够使网页实现异步交互,节省带宽和时间,提高用户体验。在使用AJAX时,需要通过解析JSON格式的数据,来获取所需要的数据。
在网页开发中,我们常常需要通过Ajax从后端获取数据并在页面中展示出来。其中,JSON是一种常用的数据格式。那么,在使用Ajax获取JSON数据后,如何将数据取出来呢?
在前端开发中,经常需要循环JSON对象数组进行数据操作。使用AJAX技术可以在不刷新页面的情况下异步获取数据。那么我们该如何循环JSON对象数组呢?下面我们通过一段代码来进行讲解。
AJAX(Asynchronous JavaScript and XML)是一种用于创建 Web 应用程序的技术,它使用 JavaScript 和 XML(或 JSON)来在后台异步传输数据。
AJAX技术被广泛应用于现代Web开发,它可以在无需重新加载页面的情况下,向服务器发出请求并更新页面,实现了异步更新的效果。而传递JSON数据是AJAX中比较常见的一种方法,下面是如何使用AJAX传递JSON数据的详细介绍。
Ajax是一种通过JavaScript和HTTP请求交互的技术,可以实现无需刷新页面的异步数据交互。在处理数据时,常常需要删除一些已存在的数据。本文将介绍如何使用Ajax删除JSON数据库中的数据。
在使用Ajax时,我们经常需要将数据格式化为JSON格式。JSON是一种轻量级数据交换格式,它以键值对的形式来表达数据。
AJAX是一种支持异步请求的技术,它可以让前端页面不用刷新就能向后台请求数据,并异步地展示给用户,提高了用户的体验感。其中,使用JSON格式化数据可以帮助我们更方便快捷地处理返回的数据。
AJAX是一种前端技术,可以通过异步请求来获取数据,并在页面上更新它们。JSON是一种轻量级的数据交换格式,因为它易于读取和编写,因此在Web应用程序中被广泛使用。AJAX传送JSON数据是一种常见的技术,可以让Web应用
在前端开发中,ajax是很常见的技术,它可以在不刷新整个页面的情况下请求服务器数据和更新部分页面。而当需要遍历多个json文件时,可以使用ajax循环遍历来实现。
AJAX技术是实现Web页面无刷新的最佳方式。其中json解析是一种常用的技术,它可以通过AJAX异步请求数据,再用json解析器将返回的json字符串解析成JavaScript对象。下面就让我们来看看如何使用ajax解析json数据。
AJAX技术可以在不刷新整个WEB页面的情况下与服务器进行数据交换,这使得在现代WEB应用中使用AJAX技术变得非常普遍。而访问JSON数组是一种非常常见的AJAX操作。在本文中,我们将向您展示如何使用AJAX技术循环遍历JSO
Ajax(Asynchronous JavaScript and XML)是一种在不重新加载整个页面的情况下更新网页的技术。它可以向服务器发送请求并接收响应,然后使用JavaScript动态地显示内容。
AJAX技术可以帮助我们实现对JSON数据库的循环读取。下面我们来介绍一下如何使用AJAX技术读取JSON数据库。
AJAX是一种在Web应用中实现局部更新的技术。而JSON是一种数据格式,非常适合用来表示数据。在AJAX中,我们经常需要从后端服务器获取JSON格式的数据,在前端页面中进行处理。那么,如何解析JSON数据呢?
AJAX是一种在不重新载入整个页面的情况下,能够更新部分页面的技术,它可以通过异步通信获取后台数据,其中JSON作为一种轻量级数据交换格式,常常被用来传递数据。在使用AJAX接收到后台传送的JSON数据后,需要进行解
在网站开发中,为了减少页面的刷新,异步加载技术成为了开发中越来越常见的一种技术,而 AJAX 技术就是一种常见的实现方式。其中,通过循环读取 JSON 数据能够实现页面内容的实时更新。
在前端开发中,经常需要从服务器获取JSON数据来展示在页面上,而循环遍历这些数据就需要使用AJAX以及JavaScript。本文将介绍如何使用AJAX和JavaScript来循环遍历JSON数据。
在前端开发中,我们常常需要通过 Ajax 请求后端接口获取数据并进行展示。而 JSON 数据则是一种常见的数据格式,因此我们需要了解如何通过 Ajax 获取 JSON 数据。
在使用ajax传递数据时,我们通常会遇到传递json数据类型的情况。那么,接下来我们就来仔细了解一下如何使用ajax传递json数据类型。