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(), DeviceIdToSearch.length(), 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&, 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).PushBack("Mio", allocator).PushBack("", 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","Mio",""]]

对象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", i, 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", itr->name.GetString(), 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).PushBack("Mio", 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, "%s %s", "Milo", "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, len, document.GetAllocator());
        // Value author(buffer, document.GetAllocator());
        memset(buffer2, 0, 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

rapidjson是什么 rapidjson是腾讯的开源Json解析框架,用C++代码实现,用于解析和生成JSON由于全部代码仅用头文件实现,因此很容易集成在项目中。根据其作者Milo Yipz所做的比较,可以看出rapidjson的性能非常可观。通过使用DOM(Document Object Model)可以很方便的将Json转化成DOM,然后查询修改,再转化为一个Json。通过rapidj...
采用pb11.5 + pbni + vs2015 + rapidjson的开源库,生成解析json,支持datawindow快速导入导出字段名有大写字母的json,支持dw导入出json时对指定字段进行des加密,并在导入到dw时时进行des解密,修改了pbvm115.dll(内存修改方法的文件,请看n_datastore的api定义), ******2022-05-11更新说明,1.增加一种dw导入导出成json的方式,以列数组方式生成json数据,可以有效降低json字符串体积;2.增加dw按字符串列统计数值功能,例如收款记录报表,一般可能按付款方式汇总付款金额,如果要在此dw按收款记录的营销人员汇总收款金额,可以用此功能,比用纯PB写代码高效的多,此功能还可以作为图形报表的数据源;3.增加跟PB程序现有数据库连结的绑定(sqlca或者自建的transobject对象)(目前只支持SNC sqlserver 2008接口),直接sql语句生成json的功能,会比dw浏览完数据再生成json要快很多。
rapidjson实现将得到的Value中的值变为string,主要应用如下场景 "{\"item_1\":{\"sub_item_1\":\"value_1\",\"sub_item_2\":\"value_2\",\"sub_item_3\":\"value_3\"},\"item_2\":\"value_2\"}中拿到item_1的值 #include "rapidjson
index.js import styles from "./styles.css" ; console . log ( styles ) ; // ".text{color:#162D3D}" npm i --save-dev parcel-plugin-css-to-string yarn add -D parcel-plugin-css-to-string 我建议对样式使用自定义扩展名,该扩展名将作为字符串导入。 对于全局样式或css-modules,这将有助于避免与.css文件的冲突。 默认资产类型css 。 您可以将自定义扩展列表添加到.parcelrc配置中。 例如: styles "age":18, "sub":["a","b"], //value是数组 "elp":[ {"a":"A","b":"B"}, //value是一个数组且里面每个元素又是一个json格式
Json 是一种轻量级数据交换格式,具有易于人阅读和编写,同时也易于机器解析和生成。相较于XML,json更小、读写更快、更易解析。另一方面,Rapidjson作为json的升级版,在效率方面,具有更好的优势。 Json语法规则 Rapdijson解析 数据读取、解析成json格式.......
RapidJSON 是一个 C++ 的 JSON 解析器及生成器。它的灵感来自 RapidXml。使用方法:RapidJSON: 首页http://rapidjson.org/zh-cn/index.html 这里 rapidjson 为作用域; 那么现在该 JSON 就会被解析至 中,成为一棵 *DOM 树 *: 让我们查询一下根 Object 中有没有 成员。由于一个 可包含不同类型的值,我们可能需要验证它的类型,并使用合适的 API 去获取其值。在此例中, 成员关联
string a;char b[4]={'a','b','c','d'};//string字符串类型的数据,用b这个字符型数组演示 for(int i=0;i<4;i++) a=a+b[i]; cout<<a; 以上可以简单的将一个字符数组转换为一个字.
我在使用VS2015进行调试(Debug x64)时,出现了如下图所示的错误信息,在网上查了大佬的介绍,最后得出结论,问题应该由代码运行时分配和释放堆内存不一致导致的。一般是指在使用std::vector&amp;lt;&amp;gt;时,会进行动态内存的分配和释放,当析构某个vector时,如果找不到vector分配的空间(也许已经释放过了),就会出现上述的问题。 我的解决方案是定位到:项目–属性–C/C+±...
#include "rapidjson/document.h" #include "rapidjson/writer.h" #include "rapidjson/stringbuffer.h" using namespace rapidjson; int main() { // 将string转换为rapidjson的Value类型 std::string jsonString = "{\"name\": \"小明\", \"age\": 18}"; Document document; document.Parse(jsonString.c_str()); // 字符串转换JSON对象 Value& name = document["name"]; // 取出name字段值 std::string nameStr = name.GetString(); // 将name字段值转换string类型 // 将rapidjson的Value类型转换string StringBuffer buffer; Writer<StringBuffer> writer(buffer); document.Accept(writer); // 将JSON对象转换字符串 std::string jsonStr = buffer.GetString(); // 将字符串存储到jsonStr中 return 0;