rapidjson 是一个 c++解析 json 的库。
使用 rapidjson 来处理 json ,发现输出和预期完全不同,以下是精简后的完整代码
#include "rapidjson/document.h"
#include "rapidjson/writer.h"
#include "rapidjson/stringbuffer.h"
#include <iostream>
#include <string>
using namespace std;
using namespace rapidjson;
void output(Document& d)
{
StringBuffer buffer;
Writer<StringBuffer> writer(buffer);
d.Accept(writer);
cout << buffer.GetString() << endl;
}
int main()
{
const char *json = "{}";
int i=0;
Document d; d.Parse(json);
Document::AllocatorType& alloc = d.GetAllocator();
//注意,这里故意写成两个 block
{
Document d2; d2.Parse("{\"A\": null}");
d.AddMember("0", d2, alloc);
output(d); //这里没有问题,输出{"0":{"A":null}}
}
{
Document d2; d2.Parse("{\"B\": null}");
d.AddMember("1", d2, alloc);
output(d); //这里就有问题了,输出{"0":{"B":null},"1":{"B":null}}
}
return 0;
}
执行后,输出如下
{"0":{"A":null}}
{"0":{"B":null},"1":{"B":null}}
即第一次 AddMember 之后,顺利把 d2 作为 d["0"]的 value ,但是第二次 AddMember 时,不仅 d["1"]=d2,连原来的 d["0"]的值也发生了改变。
如果把那两个 block 合并,这样来写
Document d2;
d2.Parse("{\"A\": null}");
d.AddMember("0", d2, alloc);
output(d); //正常输出{"0":{"A":null}}
d2.Parse("{\"B\": null}");
d.AddMember("1", d2, alloc);
output(d); //正常输出{"0":{"A:null},"1":{"B":null}}
那么一切正常,输出
{"0":{"A":null}}
{"0":{"A":null},"1":{"B":null}}
初步看了 rapidjson 里的实现, AddMember 实际执行了一个"move"的语义,即 d.AddMember("0", d2, alloc)之后, d2 携带的内容转义给了 d, 然后 d2 自己就变成了 nulltype 了,从而合并之后那个代码能正常运行是可以理解的。
但是还是不明白为什么写成两个 block 的代码,执行结果会是
{"0":{"B":null},"1":{"B":null}}
多谢。
以下是 rapidjson 里 AddMember 的实现
GenericValue& AddMember(GenericValue& name, GenericValue& value, Allocator& allocator) {
RAPIDJSON_ASSERT(IsObject());
RAPIDJSON_ASSERT(name.IsString());
Object& o = data_.o;
if (o.size >= o.capacity) {
if (o.capacity == 0) {
o.capacity = kDefaultObjectCapacity;
o.members = reinterpret_cast<Member*>(allocator.Malloc(o.capacity * sizeof(Member)));
}
else {
SizeType oldCapacity = o.capacity;
o.capacity += (oldCapacity + 1) / 2; // grow by factor 1.5
o.members = reinterpret_cast<Member*>(allocator.Realloc(o.members, oldCapacity * sizeof(Member), o.capacity * sizeof(Member)));
}
}
o.members[o.size].name.RawAssign(name);
o.members[o.size].value.RawAssign(value);
o.size++;
return *this;
}
void RawAssign(GenericValue& rhs) RAPIDJSON_NOEXCEPT {
data_ = rhs.data_;
flags_ = rhs.flags_;
rhs.flags_ = kNullFlag;
}
1
miloyip 2016-02-05 09:38:17 +08:00
RapidJSON 允许一个 Object 内有相同的 Key ,在 `AddMember()` 里不作检查。
`Document` 在 consturctor 中不给与 allocator 的时候,是自行建立一个 allocator 。那么 `Parse()` 的时候会用该 allocator 来分配内存。所以两个 block 的 第一个 block 中的 `Document d2` 在离开 block 时, allocator 会连同其分配的内存同时失效。因此之后 d 里会有 dangling pointer 。 解决方法是让 d2 采用 d 的 allocator :`Document d2(&alloc);`。 |