请问一个 rapidjson 使用问题

2015-11-21 17:01:54 +08:00
 iambic

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;
    }
5451 次点击
所在节点    问与答
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);`。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/237887

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX