
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 年 2 月 5 日 RapidJSON 允许一个 Object 内有相同的 Key ,在 `AddMember()` 里不作检查。 `Document` 在 consturctor 中不给与 allocator 的时候,是自行建立一个 allocator 。那么 `Parse()` 的时候会用该 allocator 来分配内存。所以两个 block 的 第一个 block 中的 `Document d2` 在离开 block 时, allocator 会连同其分配的内存同时失效。因此之后 d 里会有 dangling pointer 。 解决方法是让 2 采用 d 的 allocator :`Document d2(&alloc);`。 |