Reputation: 1586
I am trying to build a Document by loading two json files. The file loaded last takes the highest priority.
In the example below item1.value1
from file B
overwrites the value from file A
. item1.value2
item2
does not exist in file A
so the final Document just takes values from file B
file A:
{
level1: {
level2: {
item1: {
value1: 20,
}
}
}
file B:
{
level1: {
item2{
value1: 50
value2: 60,
}
level2: {
item1:{
value1: 40
value2: 30,
}
}
}
my goal:
{
level1: {
item2{
value1: 50
value2: 60,
}
level2: {
item1: {
value1: 40,
value2: 30,
}
}
}
Another question when I am using the range for to iterate through the Document, only level1
members are iterated, how can I go through the entire DOM?
for (auto& m : document.GetObject())
printf("Type of member %s is %s\n",
m.name.GetString(), kTypeNames[m.value.GetType()]);
Upvotes: 4
Views: 5408
Reputation: 61
The above implementation assume that srcObject
and dstObject
share the same memory because it passes the original values instead of copying them.
This means that dstObject
will hold freed objects when srcObject
is freed.
I've implemented it by copying the values:
bool mergeObjects(rapidjson::Value &dstObject, rapidjson::Value &srcObject, rapidjson::Document::AllocatorType &allocator)
{
for (auto srcIt = srcObject.MemberBegin(); srcIt != srcObject.MemberEnd(); ++srcIt)
{
auto dstIt = dstObject.FindMember(srcIt->name);
if (dstIt == dstObject.MemberEnd())
{
rapidjson::Value dstName ;
dstName.CopyFrom(srcIt->name, allocator);
rapidjson::Value dstVal ;
dstVal.CopyFrom(srcIt->value, allocator) ;
dstObject.AddMember(dstName, dstVal, allocator);
dstName.CopyFrom(srcIt->name, allocator);
dstIt = dstObject.FindMember(dstName);
if (dstIt == dstObject.MemberEnd())
return false ;
}
else
{
auto srcT = srcIt->value.GetType() ;
auto dstT = dstIt->value.GetType() ;
if(srcT != dstT)
return false ;
if (srcIt->value.IsArray())
{
for (auto arrayIt = srcIt->value.Begin(); arrayIt != srcIt->value.End(); ++arrayIt)
{
rapidjson::Value dstVal ;
dstVal.CopyFrom(*arrayIt, allocator) ;
dstIt->value.PushBack(dstVal, allocator);
}
}
else if (srcIt->value.IsObject())
{
if(!mergeObjects(dstIt->value, srcIt->value, allocator))
return false ;
}
else
{
dstIt->value.CopyFrom(srcIt->value, allocator) ;
}
}
}
return true ;
}
Upvotes: 2
Reputation: 361
I think you would try this (works for me):
void mergeObjects(rapidjson::Value &dstObject, rapidjson::Value &srcObject, rapidjson::Document::AllocatorType &allocator)
{
for (auto srcIt = srcObject.MemberBegin(); srcIt != srcObject.MemberEnd(); ++srcIt)
{
auto dstIt = dstObject.FindMember(srcIt->name);
if (dstIt != dstObject.MemberEnd())
{
assert(srcIt->value.GetType() == dstIt->value.GetType());
if (srcIt->value.IsArray())
{
for (auto arrayIt = srcIt->value.Begin(); arrayIt != srcIt->value.End(); ++arrayIt)
{
dstIt->value.PushBack(*arrayIt, allocator);
}
}
else if (srcIt->value.IsObject())
{
mergeObjects(dstIt->value, srcIt->value, allocator);
}
else
{
dstIt->value = srcIt->value;
}
}
else
{
dstObject.AddMember(srcIt->name, srcIt->value, allocator);
}
}
}
//...
rapidjson::Document from;
rapidjson::Document to;
mergeObjects(to, from, to.GetAllocator());
Note, types of nodes have to be equal. It additionally merges arrays by concatenating, not replacing.
You can iterate over entire DOM model using recursion (as above).
Hope it helps.
Upvotes: 1