Frank Liu
Frank Liu

Reputation: 1586

How to merge two json file using rapidjson

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

Answers (2)

nadavi
nadavi

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

Slav
Slav

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

Related Questions