rapidjson官方教程
时间:2020-01-28 分类:Json 作者:佚名
本教程简介文件 对象模型(Document Object Model,DOM)API。
如用法一览 中所示,可以解析一个 JSON至DOM,然后就可以轻松查询 及修改 DOM,并最终转换回JSON。
Value 及 Document
每个JSON值都储存为Value
类,而Document
类则表示整个DOM,它存储了一个 DOM树的根Value
。RapidJSON的所有公开类型及函数 都在rapidjson
命名空间中。
查询 Value
在本节中,我们会使用到example/tutorial/tutorial.cpp
中的代码 片段。
假设我们用C语言的字符串储存一个 JSON(const char* json
):
{
"hello" :
"world" ,
"t": true,128)">"f":
false ,128)">"n": null,128)">"i": 123,128)">"pi": 3.1416,128)">"a": [1,2,3,4]
}
把它解析至一个 Document
:
using namespace rapidjson;
那么现在该JSON就会被解析至document
中,成为一棵*DOM树*:
自从RFC 7159作出更新,合法JSON文件 的根可以是任何类型的JSON值。而在较早的RFC 4627中,根值只允许是Object或Array。而在上述例子中,根是一个 Object。
assert(document.IsObject());
让我们查询 一下根Object中有没有"hello"
成员。由于一个 Value
可包含不同类型的值,我们可能需要验证它的类型,并使用合适的API去获取 其值。在此例中,"hello"
成员关联到一个 JSON String。
assert(document.HasMember(
"hello" ));
assert(document[
"hello" ].I
sst ring());
printf(
"hello = %s \n" ,document[
"hello" ].GetString());
world
JSON True/False值是以bool
表示的。
"t"].IsBool());
"t =
%s \n",128)">"t"].GetBool() ?
"true" :
"false" );
true
JSON Null值可用IsNull()
查询 。
"n =
%s \n",128)">"n"].IsNull() ?
"null" :
"?" );
null
JSON Number类型表示所有数值。然而,C++需要使用更专门的类型。
"i"].IsNumber());
// 在此情况下,IsUint()/IsInt64()/IsUInt64()也会返回 true
"i"].IsInt());
"i = %d\n",128)">"i"].GetInt());
// 另一种
用法 : (int)document["i"]
"pi"].IsNumber());
"pi"].IsDouble());
"pi = %g\n",128)">"pi"].GetDouble());
i = 123
pi = 3.1416
JSON Array包含一些元素。
// 使用引用来连续访问,方便之余还更高效。
const
Value & a = document[
"a" ];
assert(a.IsArray());
for (
SizeType i = 0; i < a.Size(); i++)
"a[%d] = %d\n",i,a[i].GetInt());
a[0] = 1
a[1] = 2
a[2] = 3
a[3] = 4
注意,RapidJSON并不自动 转换各种JSON类型。例如,对一个 String的Value调用 GetInt()
是非法的。在调试模式下,它会被断言失败。在发布模式下,其行为是未定义的。
以下将会讨论有关查询 各类型的细节。
查询 Array
缺省情况下,SizeType
是unsigned
的typedef。在多数系统中,Array最多能存储2^32-1个元素。
你可以用整数字面量访问元素,如a[0]
、a[1]
、a[2]
。
Array与std::vector
相似,除了使用索引,也可使用迭代器来访问所有元素。
for (Value::ConstValueI
tera tor itr = a.Begin(); itr != a.End(); ++itr)
"%d ",itr->GetInt());
还有一些熟悉的查询 函数 :
SizeType Capacity() const
bool Empty() const
查询 Object
和Array相似,我们可以用迭代器去访问所有Object成员:
static
const
char * kTypeNames[] =
{
"Null" , "False" ,128)">"True",128)">"Object",128)"> "Array",128)">"String",128)">"Number" };
for (Value::ConstMemberI
tera tor itr = document.MemberBegin();
itr != document.MemberEnd(); ++itr)
"Type of member
%s is
%s \n",monospace; min-height:13px; white-space:pre-wrap; word-wrap:break-word; text-indent:-53px; padding-left:53px; padding-b
ott om:0px; margin:0px">
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*)
找不到成员,它会断言失败。
若我们不确定一个 成员是否存在,便需要在调用 operator[](const char*)
前先调用 HasMember()
。然而,这会导致两次查找。更好的做法是调用 FindMember()
,它能同时检查成员是否存在并返回它的Value:
Value::ConstMemberI
tera tor itr = document.FindMember(
"hello" );
if (itr != document.MemberEnd())
"
%s %s \n",itr->value.GetString());
查询 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位)。
查询 String
除了GetString()
,Value
类也有一个 GetStringLength()
。这里会解释个中原因。
根据RFC 4627,JSON String可包含Unicode字符U+0000
,在JSON中会表示为"\\u0000"
。问题是,C/C++通常使用空字符结尾字符串(null-terminated string),这种字符串把`\0'
作为结束符号。
为了符合RFC 4627,RapidJSON支持 包含U+0000
的String。若你需要处理这些String,便可使用GetStringLength()
去获得正确的字符串长度。
例如,当解析以下的JSON至Document d
之后:
"s" :
"a\u0000b" }
"a\\u0000b"
值的正确长度应该是3。但strlen()
会返回1。
GetStringLength()
也可以提高性能 ,因为用户 可能需要调用 strlen()
去分配缓冲。
此外,std::string
也支持 这个构造函数 :
string(
const
char * s,size_t count);
此构造函数 接受字符串长度作为参数。它支持 在字符串中存储空字符,也应该会有更好的性能 。
比较两个Value
你可使用==
及!=
去比较两个Value。当且仅当两个Value的类型及内容 相同,它们才当作相等。你也可以比较Value和它的原生类型值。以下是一个 例子。
if (document[
"hello" ] == document[
"n" ])
;
"hello"] ==
"world" )
;
"i"] != 123)
"pi"] != 3.14)
Array/Object顺序以它们的元素/成员作比较。当且仅当它们的整个子树相等,它们才当作相等。
注意,现时若一个 Object含有重复命名的成员,它与任何Object作比较都总会返回false
。
有多种方法 去创建值。 当一个 DOM树被创建或修改 后,可使用Writer
再次存储为JSON。
改变Value类型
当使用默 认构造函数 创建一个 Value或Document,它的类型便会是Null。要改变其类型,需调用 SetXXX()
或赋值操作,例如:
v = 10;
构造函数 的各个重载
几个类型也有重载构造函数 :
Value b(
true );
Value i(-123);
Value u(123u);
Value d(1.5);
要重建空Object或Array,可在默 认构造函数 后使用Seto bject()
/SetArray()
,或一次性使用Value(Type)
:
转移语意(Move Semantics)
在设计RapidJSON时有一个 非常特别的决定,就是Value赋值并不是把来源Value复制至目的Value,而是把把来源Value转移(move)至目的Value。例如:
Value a(123);
Value b(456);
b = a;
为什么?此语意有何优点?
最简单的答案就是性能 。对于固定大小的JSON类型(Number、True、False、Null),复制它们是简单快捷。然而,对于可变大小的JSON类型(String、Array、Object),复制它们会产生大量开销,而且这些开销常常不被察觉。尤其是当我们需要创建临时Object,把它复制至另一变量,然后再析构它。
例如,若使用正常*复制*语意:
// 把元素加进contacts数组。
o.AddMember(
"contacts" ,contacts,d.GetAllocator());
// 析构contacts。
}
那个o
Object需要分配一个 和contacts相同大小的缓冲区,对conacts做深度复制,并最终要析构contacts。这样会产生大量无必要的内存分配/释放,以及内存复制。
有一些方案可避免实质地复制这些数据,例如引用计数(reference counting)、垃圾 回收(garbage collection,GC)。
为了使RapidJSON简单及快速 ,我们选择了对赋值采用*转移*语意。这方法 与std::auto_ptr
相似,都是在赋值时转移拥有权。转移快得多简单得多,只需要析构原来的Value,把来源memcpy ()
至目标,最后把来源设置为Null类型。
因此,使用转移语意后,上面的例子变成:
// adding elements to contacts array.
// 只需 mem
cpy () contacts本身至新成员的Value(16字节)
// contacts
在这里 变成Null。它的析构是平凡的。
}
在C++11中这称为转移赋值操作(move assignment operator)。由于RapidJSON 支持 C++03,它在赋值操作采用转移语意,其它修改 形函数 如AddMember()
,PushBack()
也采用转移语意。
转移语意及临时值
有时候,我们想直接构造一个 Value并传递给一个 “转移”函数 (如PushBack()
、AddMember()
)。由于临时对象是不能转换为正常的Value引用,我们加入了一个 方便的Move()
函数 :
Document::AllocatorType& allocator = document.GetAllocator();
// a.PushBack(Value(42),allocator); // 不能通过编译
a.PushBack(
Value ().SetInt(42),allocator);
Value(42).Move(),0)">// 和上一行相同
创建String
RapidJSON提供两个String的存储策略。
cop y-string: 分配缓冲区,然后把来源数据复制至它。
const-string: 简单地储存字符串的指针。
cop y-string总是安全的,因为它拥有数据的克隆。Const-string可用于存储字符串字面量,以及用于在DOM一节中将会提到的in-situ解析中。
为了让用户 自定义 内存分配方式,当一个 操作可能需要内存分配时,RapidJSON要求用户 传递一个 allocator实例作为API参数。此设计避免了在每个Value存储allocator(或document)的指针。
因此,当我们把一个 cop y-string赋值时,调用 含有allocator的SetString()
重载函数 :
Value author;
char buffer[10];
int len = sprintf(buffer,128)">"
%s %s ",128)"> "Milo",128)">"Yip");
author.SetString(buffer,len,document.GetAllocator());
memset(buffer,sizeof (buffer));
// 清空buffer后author.GetString() 仍然包含 "Milo Yip"
在此例子中,我们使用Document
实例的allocator。这是使用RapidJSON时常用的惯用法 。但你也可以用其他allocator实例。
另外,上面的SetString()
需要长度参数。这个API能处理含有空字符的字符串。另一个 SetString()
重载函数 没有长度参数,它假设输入是空字符结尾的,并会调用 类似strlen()
的函数 去获取 长度。
最后,对于字符串字面量或有安全生命周期的字符串,可以使用const-string版本的SetString()
,它没有allocator参数。对于字符串家面量(或字符数组常量),只需简单地传递字面量,又安全又高效:
Value s;
s.SetString(
"rapidjson" );
s =
"rapidjson" ;
对于字符指针,RapidJSON需要作一个 标记 ,代表它不复制也是安全的。可以使用StringRef
函数 :
char * cstr = getenv(
"USER" );
size_t cstr_len = ...;
// s.SetString(cstr); // 这不能通过编译
StringRef(cstr,cstr_len));
StringRef(cstr,cstr_len);
修改 Array
Array类型的Value提供与std::vector
相似的API。
Clear()
Reserve(SizeType,Allocator&)
Value& PushBack(Value&,248)">template <typename T> GenericValue& PushBack(T,248)">Value& PopBack()
ValueItera tor Erase(ConstValueItera tor pos)
ValueItera tor Erase(ConstValueItera tor firs t,ConstValueItera tor last)
注意,Reserve(...)
及PushBack(...)
可能会为数组元素分配内存,所以需要一个 allocator。
以下是PushBack()
的例子:
for (
int i = 5; i <= 10; i++)
a.PushBack(i,0)">// 可能需要
调用 realloc()所以需要allocator
// 流畅接口(Fluent interface)
a.PushBack(
"Lua" ,allocator).PushBack(
"Mio" ,allocator);
与STL不一样的是,PushBack()
/PopBack()
返回Array本身的引用。这称为流畅接口(_fluent interface_)。
如果你想在Array中加入一个 非常量字符串,或是一个 没有足够生命周期的字符串(见Create String ),你需要使用cop y-string API去创建一个 String。为了避免加入中间变量,可以就地使用一个 临时值 :
// 就地Value参数
contact.PushBack(
Value (
"cop y" ,document.GetAllocator()).Move(),
document.GetAllocator());
// 显式Value参数
Value val(
"key" ,document.GetAllocator());
contact.PushBack(val,document.GetAllocator());
修改 Object
Object是键值对的集合。每个键必须为String。要修改 Object,方法 是增加 或移除成员。以下的API用来增加 城员:
Value& AddMember(Value&,Value&,Allocator& allocator)
Value& AddMember(StringRefType,248)">template <typename T> Value& AddMember(StringRefType,T value,Allocator&)
以下是一个 例子。
Value contact(kObject);
contact.AddMember(
"name" ,128)">"Milo",128)">"married",0)">true,document.GetAllocator());
使用StringRefType
作为name参数的重载版本与字符串的SetString
的接口相似。 这些重载是为了避免复制name
字符串,因为JSON object中经常会使用常数键名。
如果你需要从非常数字符串或生命周期不足的字符串创建 键名(见创建String ),你需要使用cop y-string API。为了避免中间变量,可以就地使用
contact.AddMember(
Value ().Move(),0)">// null value
// 显式参数
Value key(
// cop y string name
Value val(42);
contact.AddMember(key,val,document.GetAllocator());
移除成员有几个选择:
bool RemoveMember(const Ch* name)
:使用键名来移除成员(线性时间复杂度)。
bool RemoveMember(const Value& name)
:除了name
是一个 Value,和上一行相同。
MemberItera tor RemoveMember(MemberItera tor)
:使用迭代器移除成员(_常数_时间复杂度)。
MemberItera tor EraseMember(MemberItera tor)
:和上行相似但维持成员次序(线性时间复杂度)。
MemberItera tor EraseMember(MemberItera tor firs t,MemberItera tor last)
:移除一个 范围内的成员,维持次序(线性时间复杂度)。
MemberItera tor RemoveMember(MemberItera tor)
使用了“转移最后”手法来达成常数时间复杂度。基本上就是析构迭代器位置的成员,然后把最后的成员转移至迭代器位置。因此,成员的次序会被改变。
深复制Value
若我们真的要复制一个 DOM树,我们可使用两个APIs作深复制:含allocator的构造函数 及cop yFrom()
。
Document d;
Document::AllocatorType& a = d.GetAllocator();
Value v1(
"foo" );
// Value v2(v1); // 不容许
Value v2(v1,a);
d.SetArray().PushBack(v1,a).PushBack(v2,a);
assert(v1.IsNull() && v2.IsNull());
v2.
cop yFrom(d,0)">// 把整个document复制至v2
assert(d.IsArray() && d.Size() == 2);
v1.S
eto bject().AddMember(
"array" ,v2,monospace; min-height:13px; white-space:pre-wrap; word-wrap:break-word; text-indent:-53px; padding-left:53px; padding-b
ott om:0px; margin:0px">
d.PushBack(v1,a);
交换Value
RapidJSON也提供Swap()
。
Value b(
"Hello" );
a.Swap(b);
assert(b.IsInt());
无论两棵DOM树有多复杂,交换是很快的(常数时间)。
下一部分
本教程展示了如何询查及修改 DOM树。RapidJSON还有一个 重要概念:
流 是读写JSON的通道。流可以是内存字符串、文件 流等。用户 也可以自定义 流。
编码 定义在流或内存中使用的字符编码。RapidJSON也在内部提供Unicode转换及校验功能 。
DOM 的基本功能 已在本教程里介绍。还有更高级的功能 ,如原位(*in situ*)解析、其他解析选项及高级用法 。
SAX 是RapidJSON解析/生成 功能 的基础。学习使用Reader
/Writer
去实现更高性能 的应用程序。也可以使用PrettyWriter
去格式化JSON。
性能 展示一些我们做的及第三方的性能 测试。
技术内幕讲述一些RapidJSON内部的设计及技术。
你也可以参考常见问题、api文档 、例子及单元测试。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 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数据类型。