首页 > 编程语言 >C\C++ 使用RapidJSON库,轻松解析和生成JSON

C\C++ 使用RapidJSON库,轻松解析和生成JSON

时间:2023-11-28 10:05:01浏览次数:39  
标签:include rapidjson Value hello JSON 123 C++ document RapidJSON


简介

  RapidJSON是一个高效的C++ JSON解析器和生成器。它专注于性能和易用性,使得处理JSON数据变得简单和快速。RapidJSON支持现代的JSON特性,如嵌套对象、数组、Unicode编码和注释。它的API简洁易用,可以轻松解析和生成JSON数据。无论你的项目需要处理大量的JSON数据,还是只需要解析或生成少量的JSON数据,RapidJSON都能提供出色的性能和便利的API,成为你的理想选择。

说明文档

https://rapidjson.org/zh-cn/md_doc_pointer_8zh-cn.html

下载地址

https://github.com/Tencent/rapidjson/https://gitcode.com/mirrors/Tencent/rapidjson/tree/master

安装

RapidJSON 是只有头文件的 C++ 库。只需把 include/rapidjson 目录复制至系统或项目的 include 目录中。或者如果是用vs可以设置包含目录

C\C++ 使用RapidJSON库,轻松解析和生成JSON_c++

Value 及 Document

每个 JSON 值都储存为 Value 类,而 Document 类则表示整个 DOM,它存储了一个 DOM 树的根 Value。
RapidJSON 的所有公开类型及函数都在 rapidjson 命名空间中。

查询 Value

  • 头文件和命名空间
#include "rapidjson/document.h"
using namespace rapidjson;
  1. json字符串
{
    "hello": "world",
    "t": true ,
    "f": false,
    "n": null,
    "i": 123,
    "pi": 3.1416,
    "a": [1, 2, 3, 4]
}
  1. 代码
    将JSON字符串,解析至document 中,成为一棵 DOM 树
#include <iostream>  
#include "rapidjson/document.h"
using namespace std;
using namespace rapidjson;

int main()
{
	string json = "{ \"hello\": \"world\", \"t\": true, \"f\": false, \"n\": null, \"i\": 123, \"pi\": 3.1416, \"a\": [1, 2, 3, 4] }";
	Document document;
	document.Parse(json.c_str());

	cin.get();
	return 0;
}
  1. DOM树
  2. 判断根是不是 Object
assert(document.IsObject());

assert 是一个判断语句。参数为false时,会导致程序终止。在生产环境中,要使用其他方法来处理这种情况,例如通过返回错误代码或抛出异常。

  1. 获取成员值
  • 让我们查询一下根 Object 中有没有 “hello” 成员。
assert(document.HasMember("hello"));
  • 验证类型
assert(document["hello"].IsString());
  • 根据类型获取其值
printf("hello = %s\n", document["hello"].GetString());

输出:world

  • JSON True/False 值是以 bool 表示的。
assert(document["t"].IsBool());
printf("t = %s\n", document["t"].GetBool() ? "true" : "false");

输出:true

  • JSON Null 值可用 IsNull() 查询。
printf("n = %s\n", document["n"].IsNull() ? "null" : "?");

输出:null

  • JSON Number 类型表示所有数值。然而,C++ 需要使用更专门的类型。
assert(document["i"].IsNumber());
assert(document["pi"].IsNumber());

assert(document["i"].IsInt());   
printf("i = %d\n", document["i"].GetInt());

assert(document["pi"].IsDouble());
printf("pi = %g\n", document["pi"].GetDouble());

整型123、浮点型3.1416使用IsNumber()判断都是true

输出:
i = 123
pi = 3.1416

  • JSON Array 包含一些元素。
// 使用引用来连续访问,方便之余还更高效。
const Value& a = document["a"];
assert(a.IsArray());
for (SizeType i = 0; i < a.Size(); i++) // 使用 SizeType 而不是 size_t
        printf("a[%d] = %d\n", i, a[i].GetInt());

输出:
a[0] = 1
a[1] = 2
a[2] = 3
a[3] = 4

注意,RapidJSON 并不自动转换各种 JSON 类型。例如,对一个 String 的 Value 调用 GetInt() 是非法的,其行为是未定义的.

查询 Array

SizeType 是 unsigned int 的别名。在多数系统中,Array 最多能存储 2^32-1 个元素。
Array 与 std::vector 相似,除了使用索引,也可使用迭代器来访问所有元素。

for (Value::ConstValueIterator itr = a.Begin(); itr != a.End(); ++itr)
    printf("%d ", itr->GetInt());

当使用 C++11 功能时,你可使用范围 for 循环去访问 Array 内的所有元素。

for (auto& v : a.GetArray())
		printf("%d ", v.GetInt());

查询 Object

用迭代器去访问所有 Object 成员:

vector<string> 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()].c_str());
}

输出:
Type of member hello is String
Type of member t is True
Type of member f is False
Type of member n is Null
Type of member i is Number
Type of member pi is Number
Type of member a is Array

当使用 C++11 功能时,你可使用范围 for 循环去访问 Object 内的所有成员。

for (auto& m : document.GetObject())
    printf("Type of member %s is %s\n",
        m.name.GetString(), kTypeNames[m.value.GetType()]);

判断对象是否存在

HasMember()方法,会导致两次查找:

if(document.HasMember("hello"))
		printf("%s\n", document["hello"].GetString());

FindMember()方法,更好:

Value::ConstMemberIterator itr = document.FindMember("hello");
	if (itr != document.MemberEnd())
		printf("%s\n", itr->value.GetString());

查询 Number

查检

提取

bool IsUint()

unsigned GetUint()

32 位无符号整数

bool IsInt()

int GetInt()

32 位有符号整数

bool IsUint64()

uint64_t GetUint64()

64 位无符号整数

bool IsInt64()

int64_t GetInt64()

64 位有符号整数

bool IsDouble()

double GetDouble()

64 位双精度浮点数

注意,一个整数可能用几种类型来提取,而无需转换。例如,一个名为 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 可能会丧失精度( int64_t 最大值有19 位有效数字,uint64_t最大值有 20 位有效数字,都超过了 double 15位有效数字的限制)。

查询 String

c++string字符串把 '\0' 作为结束符号,如果json的值中带有这个字符,则需要用GetStringLength()获取正确的长度。

if (document.HasMember("hello") && document["hello"].IsString())
	{
		SizeType len = document["hello"].GetStringLength();
		string str(document["hello"].GetString(), len);
		printf("%s %d\n", str.c_str(), len);
	}

输出:
world 5

比较两个 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 顺序以它们的元素/成员作比较。当且仅当它们的整个子树相等,它们才当作相等。
另外需要注意的是若一个 Object 含有重复命名的成员,它与任何 Object 作比较都总会返回 false。

创建/修改值

当一个 DOM 树被创建或修改后,可使用 Writer 再次存储为 JSON。

改变 Value 类型和值

代码

document["t"].SetInt(666);
document["t"] = 666;     // 简写,和上面的相同
Value t(666);           //使用Value的构造函数
document["t"] = t;

完整代码

#include <iostream>  
#include <string>  
#include "rapidjson/document.h"    
#include "rapidjson/writer.h"    
#include "rapidjson/stringbuffer.h"    
using namespace std;
using namespace rapidjson;

int main() {
    string json = "{ \"hello\": \"world\", \"t\": true, \"f\": false, \"n\": null, \"i\": 123, \"pi\": 3.1416, \"a\": [1, 2, 3, 4] }";
    Document document;
    document.Parse(json.c_str());
    修改值
    document["hello"] = "LiHai"; // 修改 "hello" 的值为 "earth"    
    document["t"] = 666; // 修改 "t" 的值为666,同时修改类型    
   
    //使用 Writer 再次存储为 JSON
    StringBuffer buffer;
    Writer<StringBuffer> writer(buffer);
    document.Accept(writer);
    string jsonNew = buffer.GetString();
	
	//打印输出
    cout << json << endl;
    cout << jsonNew << endl;

    cin.get();
    return 0;
}

输出:
{ “hello”: "world", “t”: true, “f”: false, “n”: null, “i”: 123, “pi”: 3.1416, “a”: [1, 2, 3, 4] }
{“hello”:"LiHai",“t”:666,“f”:false,“n”:null,“i”:123,“pi”:3.1416,“a”:[1,2,3,4]}

构造函数的各个重载

几个类型也有重载构造函数:

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);

转移语义(Move Semantics)

在设计 RapidJSON 时有一个非常特别的决定,就是 Value 赋值并不是把来源 Value 复制至目的 Value,而是把来源 Value 转移(move)至目的 Value。例如:

Value a(123);
Value b(456);
b = a;         // a 变成 Null,b 变成数字 123。

C\C++ 使用RapidJSON库,轻松解析和生成JSON_c++_02

  • 为什么?此语义有何优点?

赋值时转移拥有权。转移快得多简单得多,只需要析构原来的 Value,把来源 memcpy() 至目标,最后把来源设置为 Null 类型。

深复制 Value

Value a(123);  
Value b(456);  
b.CopyFrom(a, document.GetAllocator());  // a 仍然是数字 123,b 现在是数字 123。

交换 Value

RapidJSON 也提供 Swap()。

Value a(123);
Value b("Hello");
a.Swap(b);

修改 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

以下是 PushBack() 的例子:

#include <iostream>  
#include <string>  
#include "rapidjson/document.h"    
#include "rapidjson/writer.h"    
#include "rapidjson/stringbuffer.h"   

using namespace std;
using namespace rapidjson;

int main() 
{
    string json = "{ \"hello\": \"world\", \"t\": true, \"f\": false, \"n\": null, \"i\": 123, \"pi\": 3.1416, \"a\": [1, 2, 3, 4] }";
    Document document;
    document.Parse(json.c_str());
    
    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("Li", allocator).PushBack("Hai", allocator);

    document["a"] = a;

    StringBuffer buffer;
    Writer<StringBuffer> writer(buffer);
    document.Accept(writer);
    string jsonNew = buffer.GetString();

    cout << json << endl;
    cout << jsonNew << endl;

    cin.get();
    return 0;
}

输出:
{ …, “a”: [1, 2, 3, 4] }
{…,“a”:[5,6,7,8,9,10,“Li”,“Hai”]}

流畅接口(fluent interface

与 STL 不一样的是,PushBack()/PopBack() 返回 Array 本身的引用。这称为流畅接口(fluent interface)。

a.PushBack("Li", allocator).PushBack("Hai", allocator);

修改 Object

Object 是键值对的集合。每个键必须为 String。要修改 Object,方法是增加或移除成员。以下的 API 用来增加成员:

Value& AddMember(Value&, Value&, Allocator& allocator)Value& AddMember(StringRefType, Value&, Allocator&)template <typename T> Value& AddMember(StringRefType, T value, Allocator&) 以下是一个例子。

Value contact(kObject);
contact.AddMember("name", "Milo", document.GetAllocator());
contact.AddMember("married", true, document.GetAllocator());

使用 StringRefType 作为 name 参数的重载版本与字符串的 SetString 的接口相似。 这些重载是为了避免复制 name 字符串,因为 JSON object 中经常会使用常数键名。

如果你需要从非常数字符串或生命周期不足的字符串创建键名(见 创建 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):移除一个范围内的成员,维持次序(线性时间复杂度)。
MemberIterator RemoveMember(MemberIterator) 使用了“转移最后”手法来达成常数时间复杂度。基本上就是析构迭代器位置的成员,然后把最后的成员转移至迭代器位置。因此,成员的次序会被改变。


标签:include,rapidjson,Value,hello,JSON,123,C++,document,RapidJSON
From: https://blog.51cto.com/wangpaifeixingy/8595246

相关文章

  • C++ 字符串编码转换封装函数,UTF-8编码与本地编码互转
    简介字符串编码转换封装函数,UTF-8编码与本地编码互转。中文乱码的解决方法有时候我们会遇到乱码的字符串,比如:古文码可能是用GBK方式读取UTF-8编码的中文导致的,用下面的Utf8ToLocal(stringstr)函数转换一下就可以了。口字码可能是因为以UTF-8的方式读取GBK编码的中文导致的,用下面......
  • C++ 01.学习C++的意义-狄泰软件学院
    一些历史UNIX操作系统诞生之初是用汇编语言编写的随着UNIX系统的发展,汇编语言的开发效率成为瓶颈,所以需要一个新的语言替代汇编语言1971年通过对B语言改良,使其能直接产生机器代码,C语言诞生UNIX使用C语言重写,同时C语言在实践中不断升级完善。C语言的特点没有深思熟虑的设计过程残留......
  • C++ 修改文件创建时间、修改时间属性
    简介        修改文件创建时间、修改时间、大小等属性。        博客《C++获取文件创建时间、修改时间、大小等属性》分享后,好兄弟“古月”发来一段代码,说可以修改文件的创建时间等。测试了一下真可以,下面是运行效果和代码:代码#include<windows.h>#include<f......
  • C++ 使用Windows的API CreateDirectory 创建多层级文件夹
    简介使用Windows的API创建多层级文件夹效果代码#include<windows.h>#include<direct.h>#include<iostream>#include<string>#include<sstream>#include<vector>//创建多层级文件夹boolCreateDir(conststd::string&path){ std::......
  • C++ 获取文件创建时间、修改时间、大小等属性
    简介获取文件创建时间、修改时间、大小等属性代码#include<iostream>#include<string.h>#include<time.h>voidmain(){std::stringfilename="E:\\LiHai123.txt";struct_statstat_buffer;intresult=_stat(filename.c_str(),&stat_b......
  • C++ 获取网卡名称和IP地址
    描述这是获取网卡名称和IP地址的代码示例,参考自。原文描述得比较详细,感谢博主分享。原文代码中没有输出网卡的物理地址,下面的代码进行了补充,并在win10上运行正常。代码//#include<WinSock2.h>#include<Iphlpapi.h>#pragmacomment(lib,"Iphlpapi.lib")//需要添加Iphlpapi.lib......
  • C\C++ 设置Visual Studio编译器使用C++17标准
    文章作者:里海简介:        使用ISOC++17标准可以为开发人员带来许多好处,包括更简洁的代码、更高的运行效率、更好的硬件支持、更好的兼容性和可移植性,以及更好的多线程编程支持等。那么如何设置vs使用c++标准呢?下面是方法。注意需要vs2017及以上版本。方法:打开VisualStud......
  • C++ 33.C++中的字符串类-狄泰软件学院
    C语言字符串的历史C语言不支持真正意义上的字符串C语言用字符数组和一组函数实现字符串操作C语言不支持自定义类型,因此无法创建字符串类型当年C语言主要用于开发UNIX操作系统,处理字符串的情况少,所以在当时的背景下没有让C语言中内置一个字符串类型。后来C语言越用越广泛,没办法只能......
  • C\C++ 专栏目录
    个人总结序号内容笔记01C++获取网卡名称和IP地址笔记链接02C++设置VisualStudio编译器使用C++17标准笔记链接03C++使用Pugixml库,轻松处理XML文件笔记链接04C++使用ShellExecuteEx调exe程序笔记链接05C++使用exception类,抛出自定义异常并捕获笔记链接06C++使用soc......
  • Java 将JSON数组转成List对象集合
     一、从对象列表中提取并组装JSON字段的数据:(工具类)publicclassJsonMsgUtils<T>{/***从对象列表中提取并组装JSON字段的数据。**@paramlogs包含对象的列表*@paramtargetClass目标对象类型,表示JSON消息的结构......