2

I have:

QJsonObject obj1({"bla" : "lab"})
QJsonObject obj2({"bla2" : "lab2"})

and I need:

QJsonObject obj3({"bla" : "lab", "bla2" : "lab2"})

Or in JSON:

{
    "bla" : "lab"
}

{
    "bla2" : "lab2"
}

And I need:

{
    "bla" : "lab",
    "bla2" : "lab2"
}

How to achieve that?

scopchanov
  • 7,966
  • 10
  • 40
  • 68
dosvarog
  • 694
  • 1
  • 6
  • 20
  • Do you need to merge recursively? Have you tried looping over the individual objects and inserting the key-value pairs into `obj3`? – Botje Nov 03 '20 at 15:21

4 Answers4

5
QJsonObject obj3(obj1);
for (auto it = obj2.constBegin(); it != obj2.constEnd(); it++) {
    obj3.insert(it.key(), it.value());
}
Botje
  • 26,269
  • 3
  • 31
  • 41
2

Solution

I prefer to avoid explicit loops, so my solution would be to use a convertion to and from QVariantMap, aka QMap<QString, QVariant>:

  1. Use QJsonObject::toVariantMap to convert all JSON objects to QVariantMap

  2. Use QMap::insert to insert all maps into one

  3. Use QJsonObject::fromVariantMap to convert the resulting map back to JSON object

Note: The proposed solution would work best if all JSON objects contain unique keys, because the documentation states:

If map contains multiple entries with the same key then the final value of the key is undefined.

Example

Here is a simple example I have prepared for you to demonstrate how the proposed solution could be implemented:

QJsonObject json1{{"foo_key", "foo_value"}};
QJsonObject json2{{"moo_key", "moo_value"}, {"boo_key", "boo_value"}};
QVariantMap map = json1.toVariantMap();

map.insert(json2.toVariantMap());

qDebug() << QJsonObject::fromVariantMap(map);

Result

This example produces the following result:

QJsonObject({"boo_key":"boo_value","foo_key":"foo_value","moo_key":"moo_value"})
scopchanov
  • 7,966
  • 10
  • 40
  • 68
  • I used your proposed solution, it suites my situation. Only thing is I couldn't use `insert` for some reason, so I used `map.unite(json2.toVariantMap())`. – dosvarog Nov 05 '20 at 12:33
  • 1
    @dosvarog, you did not specify a Qt version. `insert` was introduced in Qt 5.15. On the other hand, `unite` is obsolete. Anyway, I am glad to help. – scopchanov Nov 05 '20 at 12:38
  • 1
    You are correct. It always slips my mind that Qt changes quite often. My version is 5.12. – dosvarog Nov 05 '20 at 12:45
  • 1
    @dosvarog, As long as it works for you, it is ok, I think. Probably it is a good idea to put a comment in that place to remind you to change it, if you ever upgrade to 5.15 or newer. – scopchanov Nov 05 '20 at 12:47
2

Extended version, which also merges objects of same name and adds array elements as well (if present with same name in both objects):

void mergeJson(QJsonObject& src, const QJsonObject& other)
{
    for(auto it = other.constBegin(); it != other.constEnd(); ++it)
    {
        if(src.contains(it.key()))
        {
            if(src.value(it.key()).isObject() && other.value(it.key()).isObject())
            {
                QJsonObject one(src.value(it.key()).toObject());
                QJsonObject two(other.value(it.key()).toObject());

                mergeJson(one, two);
                src[it.key()] = one;
            }
            else if(src.value(it.key()).isArray() && other.value(it.key()).isArray())
            {
                QJsonArray arr = other.value(it.key()).toArray();
                QJsonArray srcArr = src.value(it.key()).toArray();
                for(int i = 0; i < arr.size(); i++)
                    srcArr.append(arr[i]);
                src[it.key()] = srcArr;
            }
        }
        else
            src[it.key()] = it.value();
    }
}

If src and other have a field with the same name (except arrays and objects, see top), src will be used.

src:

{
   "arr":[
      {
         "fieldOne":"dqwd",
         "fieldTwo":"dqwd2"
      },
      {
         "fieldOne":"dqwd",
         "fieldTwo":"dqwd2"
      },
      {
         "fieldOne":"dqwd",
         "fieldTwo":"dqwd2"
      }
   ],
   "fieldOne":"dqwd",
   "fieldTwo":"dqwd2",
   "two":{
      "fieldOne":"dwqwfw",
      "fieldTwo":"grew",
      "fregtegergwedffe":{
         "sdqqwd":"wdqfrg"
      }
   }
}

other:

{
   "arr":[
      {
         "fieldOne":"dwqwfw",
         "fieldTwo":"kjhgf",
         "qwdqwd":"grew"
      }
   ],
   "fieldOne":"rfgwef",
   "grege":"gfewrfew",
   "grwefege":"fewfgrew",
   "two":{
      "fieldOne":"dwqwfw",
      "fieldTwo":"kjhgf",
      "qwdqwd":"grew"
   }
}

merged/src after call:

{
   "arr":[
      {
         "fieldOne":"dqwd",
         "fieldTwo":"dqwd2"
      },
      {
         "fieldOne":"dqwd",
         "fieldTwo":"dqwd2"
      },
      {
         "fieldOne":"dqwd",
         "fieldTwo":"dqwd2"
      },
      {
         "fieldOne":"dwqwfw",
         "fieldTwo":"kjhgf",
         "qwdqwd":"grew"
      }
   ],
   "fieldOne":"dqwd",
   "fieldTwo":"dqwd2",
   "grege":"gfewrfew",
   "grwefege":"fewfgrew",
   "two":{
      "fieldOne":"dwqwfw",
      "fieldTwo":"grew",
      "fregtegergwedffe":{
         "sdqqwd":"wdqfrg"
      },
      "qwdqwd":"grew"
   }
}
1

You can iterate over all jsons that you need to merge and then over their elements and insert them into the new json:

  QJsonObject obj1({{"bla1", "lab1"}});
  QJsonObject obj2({{"bla2", "lab2"}});
  QJsonObject obj34({{"bla3", "lab3"}, {"bla4", "lab4"}});
  QJsonObject result;
  for (const auto& json : {obj1, obj2, obj34})
  {
    for (auto it = json.begin(); it != json.end(); it++)
    {
      result.insert(it.key(), it.value());
    }
  }
  for (auto it = result.begin(); it != result.end(); it++)
  {
    qDebug() << it.key() << ": " << it.value();
  }

Probably not the most efficient, though.

pptaszni
  • 5,591
  • 5
  • 27
  • 43