Search code examples
rapidjson

Copy member using jsonwriter


How to copy a rapidjson::Value using rapidjson::writer? I found several solutions when using a Document - but not when using a writer.

E.g.: How can I add members to a rapidjson document using integers as the key/name?

So, I want something like this to work:

rapidjson::CrtAllocator _crtAllocator;
rapidjson::GenericStringBuffer<rapidjson::UTF8<>> _jsonBuffer;
rapidjson::PrettyWriter<rapidjson::StringBuffer> _jsonWriter;

void GeojsonWriter::addMember(std::string name, rapidjson::Value& value) {
    _jsonWriter.AddMember(name, value, _crtAllocator);
}

An example usage is in my case to copy the "properties" object of a Geojson feature to another feature (which was created from a different geometry, but should be assigned a copy of the "original" properties)

Update

I've implemented the method as in the answer below. Though I face random crashes when accessing itr->name.GetString() below. The crash leads to std string... Though sometimes the same data works fine. This looks much like a memory allocation issue - but I have no trails where to look at.

Crash in std string here (though less relevant probably): static inline size_t length(const char_type* __s) {return strlen(__s);}

void copyGeojsonProperties(GeojsonWriter &writer, const rapidjson::Value* properties) {
    for (rapidjson::Value::ConstMemberIterator itr = properties->MemberBegin(); itr != properties->MemberEnd(); ++itr) {
        if (itr->name.IsString()) {
            const char* cstr = itr->name.GetString();
            if (!cstr || !itr->name.Empty())
                continue;
            std::string key = itr->name.GetString();
            const rapidjson::Value& val = itr->value;
            writer.addMember(key, val);
        }
    }
}

Update 2

I found a workaround. I have multiple input json documents form where I use value.CopyFrom(..., document.GetAllocator()). The issue was that the document was freed when the json was read (I copy objects from different jsons, and later write them into another json). Now I keep a reference to the pointer, to prevent the allocator being freed.

It isn't obvious though that it's required to keep the allocator around even when the value was copied.


Solution

  • If the writer has already been called StartObject(), then you only need to:

    void GeojsonWriter::addMember(const std::string& name, const rapidjson::Value& value) {
        _jsonWriter.Key(name.c_str(), name.size());
        value.Accept(_jsonWriter);
    }