3

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()]);
Frank Liu
  • 1,466
  • 3
  • 23
  • 36

2 Answers2

2

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 ;
}
JustSomeGuy
  • 3,677
  • 1
  • 23
  • 31
nadavi
  • 51
  • 2
1

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.

Slav
  • 361
  • 2
  • 11
  • Be careful `mergeObjects` will move the children of `srcObject` to `dstObject`. After call this function, `from` will be an empty object. – Alexander Chen Feb 21 '22 at 01:56