RapidJson踩坑记录

article/2025/10/4 8:36:00

用于记录RapidJson使用中的坑位,持续更新。关于rapidjson的详细说明,可以参加参考文档:http://rapidjson.org/zh-cn/md_doc_tutorial_8zh-cn.html#CreateString

1、添加字符串元素

现象:

#include "rapidjson/document.h"
#include "rapidjson/prettywriter.h"
#include "rapidjson/stringbuffer.h"
#include <iostream>using namespace std;
using namespace rapidjson;int main() {Document doc;doc.SetObject();Document::AllocatorType &allocator = doc.GetAllocator();string value1 = "value1";doc.AddMember("key1", StringRef(value1.c_str(), value1.size()), allocator);string v = doc["key1"].GetString();cout << v.c_str() << endl; // 输出为value1value1 = "abc";v = doc["key1"].GetString();cout << v.c_str() << endl; // 输出为abcsystem("pause");return 0;
}

输出结果证明,上述操作为字符串浅拷贝。所以如果有下面这样的代码,输出将是不确定的值。

void addMem(Document& doc) {string value = "123456";doc.AddMember("key", StringRef(value.c_str(), value.size()), doc.GetAllocator());
}int main() {Document doc;doc.SetObject();addMem(doc);string v = doc["key"].GetString();cout << v.c_str() << endl; //此处由于局部变量被释放,将输出乱码system("pause");return 0;
}

原因:

分析原因,首先看两点:

1、StringRef对于字符串的操作

template<typename CharType>
inline GenericStringRef<CharType> StringRef(const CharType* str, size_t length) {return GenericStringRef<CharType>(str, SizeType(length));
}

这个源码中对于StringRef函数的定义,返回一个GenericStringRef对象。从对应的构造方法中可以看出,其只是暴力的把地址的值浅拷贝到GenericStringRef内维护的char* 变量s中,长度拷贝到length中:

GenericStringRef(const CharType* str, SizeType len): s(RAPIDJSON_LIKELY(str) ? str : emptyString), length(len) { RAPIDJSON_ASSERT(str != 0 || len == 0u); }

 2、AddMember的实现

在看AddMember是怎么实现的。

GenericValue& AddMember(StringRefType name, StringRefType value, Allocator& allocator) {GenericValue v(value);return AddMember(name, v, allocator);}

首先,通过StringRef返回的StringRefType类型,构造一个GenericValue。

explicit GenericValue(StringRefType s) RAPIDJSON_NOEXCEPT : data_() { SetStringRaw(s); }

然后,构造时,调用了SetStringRaw方法。

RAPIDJSON_FORCEINLINE const Ch* SetStringPointer(const Ch* str) { return RAPIDJSON_SETPOINTER(Ch, data_.s.str, str); }

经过一系列调用,最终调用到这里。查看RAPIDJSON_SETPOINTER宏,可以看到,依然是暴力的浅拷贝。

#define RAPIDJSON_SETPOINTER(type, p, x) (p = (x))

 有了一个浅拷贝的GenericValue,最后看AddMember。同样的,经过一系列调用,最后的RawAssign函数如下:

void RawAssign(GenericValue& rhs) RAPIDJSON_NOEXCEPT {data_ = rhs.data_;// data_.f.flags = rhs.data_.f.flags;rhs.data_.f.flags = kNullFlag;
}

 于是我们看到的是,从始至终,一直是浅拷贝的AddMember。所以当给doc添加字符串value时,不要添加局部变量作为入参。同理,key也不要。

正确写法:

1、doc.AddMember("key", "value", doc.GetAllocator()); //直接添加字符串常量,常量区不会释放

2、构造一个Value,添加到doc中,代码如下:

void addMem(Document& doc) {string value = "123456";Value v(kStringType);v.SetString(value.c_str(), value.size(), doc.GetAllocator()); //这里很重要,必须要传递allocator作为参数,否则依然为浅拷贝。doc.AddMember("key", v, doc.GetAllocator());
}

注意allocator的传递。

2、Document Value的拷贝

先看如下代码:

int main() {Document doc;doc.SetObject();doc.AddMember("key1", "value1", doc.GetAllocator());doc.AddMember("key2", "value2", doc.GetAllocator());cout << "key1:" << (doc["key1"].IsNull() ? "NULL" : doc["key1"].GetString()) << endl;cout << "key2:" << (doc["key2"].IsNull() ? "NULL" : doc["key2"].GetString()) << endl;system("pause");return 0;
}

输入出结果如所期,"value1","value2"。一切看上去都很和谐。然后加上一个key3。代码如下

int main() {Document doc;doc.SetObject();doc.AddMember("key1", "value1", doc.GetAllocator());doc.AddMember("key2", "value2", doc.GetAllocator());doc.AddMember("key3", doc["key1"], doc.GetAllocator());cout << "key1:" << (doc["key1"].IsNull() ? "NULL" : doc["key1"].GetString()) << endl;cout << "key2:" << (doc["key2"].IsNull() ? "NULL" : doc["key2"].GetString()) << endl;cout << "key3:" << (doc["key3"].IsNull() ? "NULL" : doc["key3"].GetString()) << endl;system("pause");return 0;
}

想做的事也很简单,添加一个key3的key,value设置成与key1相同。于是很开心的看是编译运行,然而得到的结果却兵不正确。

key1变成了null,瓦斯则法克。看源码。

其实源码在上个问题中已经贴上了。RapidJson重载了6个AddMember函数,6个函数经过一系列的封装,最终调用的都是如下函数原型:

GenericValue& AddMember(GenericValue& name, GenericValue& value, Allocator& allocator) {RAPIDJSON_ASSERT(IsObject());RAPIDJSON_ASSERT(name.IsString());ObjectData& o = data_.o;if (o.size >= o.capacity)MemberReserve(o.capacity == 0 ? kDefaultObjectCapacity : (o.capacity + (o.capacity + 1) / 2), allocator);Member* members = GetMembersPointer();members[o.size].name.RawAssign(name);members[o.size].value.RawAssign(value);o.size++;return *this;}

而RawAssign的实现,上面已经贴过了。在上个问题中,我们关注的是浅拷贝问题,而在这里我们重点关注这一句话:

rhs.data_.f.flags = kNullFlag;

原值的flag被置为kNullFlag。这个值厉害了,判断doc是否为空就靠它。

bool IsNull()   const { return data_.f.flags == kNullFlag; }

试图去取一个null,会抛出异常,所以做Value直接Add的时候要格外当心。


http://chatgpt.dhexx.cn/article/F9xvBean.shtml

相关文章

c++ rapidjson

下面rapid json代码已在vs2017验证&#xff0c;特别地&#xff0c;用rapid json可以解析中文字符串&#xff0c;不会中文乱码。 第一步&#xff1a;去https://github.com/Tencent/rapidjson/上下载头文件&#xff0c;只需要其中的include文件夹。也可以在csdn上下载&#xff1…

RapidJSON简介及使用

RapidJSON是腾讯开源的一个高效的C JSON解析器及生成器&#xff0c;它是只有头文件的C库。RapidJSON是跨平台的&#xff0c;支持Windows, Linux, Mac OS X及iOS, Android。它的源码在GitHub - Tencent/rapidjson: A fast JSON parser/generator for C with both SAX/DOM style …

JSON--rapidjson介绍

JSON--rapidjson 1 RapidJSON简介2 C/C Json库对比一致性解析时间解析内存Stringify Time&#xff08;string 2 json&#xff09;Prettify Time&#xff08;美化格式时间&#xff09;代码大小 3 几个重点库介绍rapidjsonnlohmann-jsonjsoncppcjson 参考 1 RapidJSON简介 Rapid…

RapidJSON入门:手把手教入门实例介绍

RapidJSON优点 跨平台 编译器&#xff1a;Visual Studio、gcc、clang 等 架构&#xff1a;x86、x64、ARM 等 操作系统&#xff1a;Windows、Mac OS X、Linux、iOS、Android 等 容易安装 只有头文件的库。只需把头文件复制至你的项目中。 独立、最小依赖 不需依赖 STL、BOOST …

oracle listagg如何去重

listagg去重 去重思路&#xff1a;利用listagg会忽略null值的特点 按ENTITY_GROUP_RRN 分组&#xff0c;用 listagg 分别合并 EQPT_ID 与 STATION_ID &#xff0c;同时要求去重 表 T_TEST 数据如下&#xff1a; EQPT_IDENTITY_GROUP_RRNSTATION_IDTOOL-00110493721JITAI-1TO…

mysql listagg within_Oracle的 listagg() WITHIN GROUP ()函数使用

1.使用条件查询 查询部门为20的员工列表 -- 查询部门为20的员工列表 SELECT t.DEPTNO,t.ENAME FROM SCOTT.EMP t where t.DEPTNO 20 ; 效果&#xff1a; 2.使用 listagg() WITHIN GROUP () 将多行合并成一行 SELECT T .DEPTNO, listagg (T .ENAME, ,) WITHIN GROUP (ORDER …

listagg结果去重

最近在一个项目中用到了listagg方法&#xff0c;但是在组合结果中出现有重复的情况。默认的结果如下 于是我就写了一个方法对listagg的结果去重&#xff0c;也可以对该格式的字符串去重&#xff0c;方法如下 create or replace function listaggpure(targetStr varchar2,seper…

Oracle函数之listagg函数

语法 有点难以看懂&#xff0c;个人理解listagg是list aggregate的缩写&#xff08;错了勿喷&#xff09;&#xff0c;也就是列表总计&#xff0c;聚合的意思。 官方文档解释为&#xff1a; LISTAGG orders data within each group specified in the ORDER BY clause and then …

listagg()行转列函数

--基础数据 DROP TABLE "ZYH_TEST"; CREATE TABLE "ZYH_TEST" ("ID" NUMBER(19) NOT NULL ,"NAME" VARCHAR2(255 BYTE) ,"CREATETIME" DATE ,"SCORE" NUMBER ,"CLASSID" VARCHAR2(255 BYTE) )INSERT I…

oracle listagg支持,PostgreSQL行列转换(兼容oracle listagg)

oracle11g开始支持的listagg函数替代了wmconcat来实现行列转换的功能。 listagg函数的用法: oracle行列转换例子: —建表https://www.cndba.cn/foucus/article/3929https://www.cndba.cn/foucus/article/3929 SQL> create table b (id number,name varchar2(20)); Table c…

mysql listagg函数_SQLSERVER中的ListAGG

跃然一笑 MySQLSELECT FieldA , GROUP_CONCAT(FieldB ORDER BY FieldB SEPARATOR ,) AS FieldBs FROM TableName GROUP BY FieldA ORDER BY FieldA;Oracle&DB2SELECT FieldA , LISTAGG(FieldB, ,) WITHIN GROUP (ORDER BY FieldB) AS FieldBs FROM TableName GRO…

mysql listagg within_Oracle函数之LISTAGG

最近在学习的过程中&#xff0c;发现一个挺有意思的Oracle函数&#xff0c;它可实现对列值的拼接。下面我们来看看其具体用法。 最近在学习的过程中&#xff0c;发现一个挺有意思的Oracle函数&#xff0c;它可实现对列值的拼接。下面我们来看看其具体用法。 用法&#xff1a; 对…

listagg

1.创建数据表&#xff0c;准备测试数据 CREATE TABLE MK_STUDENT(ID NUMBER(18) NOT NULL,STU_NAME VARCHAR2(200),STU_SCORE VARCHAR2(50),PRIMARY KEY (ID) ); ALTER TABLE MK_STUDENT ADD KEMU VARCHAR2(200);INSERT INTO MK_STUDENT (ID,STU_NAME,STU_SCORE,KEMU) VALUES …

listagg( )详解

想象一个场景&#xff0c;现实生活中一个人有许多手机号已是常态&#xff0c;数据库中也会有类似的结构。 大家肯定想知道listagg()有什么样的效果&#xff1a; 案列分析 一个表中有许多数据&#xff0c;名字叫张三的有许多手机号。希望查询结果出来是分组且清晰。 select …

Oracle列转行函数 Listagg()详解

详解 listagg()函数可以实现多列记录聚合为一条记录&#xff0c;从而实现数据的压缩、致密化&#xff08;data densification&#xff09; 基本用法 像聚合函数一样&#xff0c;通过Group by语句&#xff0c;把每个Group的一个字段&#xff0c;拼接起来 LISTAGG(XXX,XXX) WI…

Oracle 中 LISTAGG 函数的介绍以及使用

LISTAGG 函数介绍 listagg 函数是 Oracle 11.2 推出的新特性。 其主要功能类似于 wmsys.wm_concat 函数&#xff0c; 即将数据分组后&#xff0c; 把指定列的数据再通过指定符号合并。LISTAGG 使用 listagg 函数有两个参数&#xff1a;1、 要合并的列名2、 自定义连接符号☆L…

矩阵乘以其矩阵转置求导-数学

20210703 矩阵论 https://zhuanlan.zhihu.com/p/288541909?utm_sourcewechat_session 矩阵运算法则 20210529 https://blog.csdn.net/Lisa_Ren_123/article/details/81983785 矩阵转置求导 https://jingyan.baidu.com/article/da1091fb69f0b7027849d612.html

matlab中怎么求矩阵的转置

第一步我们首先需要知道matlab中矩阵后面加单引号是共轭转置&#xff0c;加点和单引号是转置&#xff0c;如下图所示&#xff1a; matlab中怎么求矩阵的转置 第二步在matlab命令行窗口中输入“ A[1 2 4;5 6 7]”&#xff0c;如下图所示&#xff1a; matlab中怎么求矩阵的转…