简体   繁体   中英

Access parsed nested JSON via keys in Qt C++

Here is an example of JSON:

 {
  "sets": [
   {
   "id": "123",
   "start": "Alpha",
   "end": "Theta",
   "units": [
   {
      "id": " A",
      "name": "nameA",
      "position": 1,
      "capacity": 7
    },
    {
      "id": "nameB",
      "name": "WWW4561",
      "position": 2,
      "capacity": 15
    }
    ]
  },
  {
    "id": "456",
    "start": "Beta",
    "end": "Zeta",
    "units": [
    {
      "id": "B",
      "name": "nameB",
      "position": 1,
      "capacity": 9
    },
    {
      "id": "F",
      "name": "nameF",
      "position": 1,
      "capacity": 18
    }
    ]
   }
  ]
}

I want to store the parsed data in a container such that I will be able to access any unit item from units given the start and end values of the sets . If I am not mistaken this should be done using a dictionary .

Currently, I can only iterate through the sets and the units only. Here is what I tried:

QNetworkReply *json_reply;
QJsonObject jsonObject= QJsonDocument::fromJson(json_reply->readAll()).object();
QJsonArray sets_array = jsonObject["sets"].toArray();
QJsonArray units_array = jsonObject[""].toArray(); // explicitly empty array

int idx = 0;
for (const QJsonValue& set_val: sets_array)
{
    QJsonObject set_loopObj = set_val.toObject();

    qDebug() << "\nset" << idx;
    qDebug() << " id:" << set_loopObj["id"].toString();
    qDebug() << "start:" << set_loopObj["start"].toString();
    qDebug() << "end:" << set_loopObj["end"].toString();

        units_array.append(set_loopObj["units"].toArray());

    int idy = 0;
    for (const QJsonValue& unit_val: units_array)
    {
        QJsonObject unit_loopObj = unit_val.toObject();

        qDebug() << "\nunit";
        qDebug() << "id:" << unit_loopObj["id"].toString();
        qDebug() << "name:" << unit_loopObj["name"].toString();
        qDebug() << "position:" << unit_loopObj["position"].toDouble();
        qDebug() << "capacity:" << unit_loopObj["capacity"].toDouble();

        ++idy;
    }
    ++idx;
}
qDebug() << "\nsets_array" << sets_array;
qDebug() << "\nunits_array" << units_array;

UPDATE

For example, I expect that by giving "Alpha","Theta" to get both units of the first set as an array so that I should be able to access individual units by giving an indice (eg 1 for the 2nd unit).

I would also want to get the rest of the sets info ( id ) as well when providing the "Alpha, Theta" keys.

Please provide code in your answers cause I am newbie.

I'd definitely break this into two tasks. The first subtask to to actually grab the appropriate object by its start and end.

QJsonObject find_by_start_and_end(const QJsonObject& obj, 
                                  const QString& start, 
                                  const QString& end) {

    QJsonArray sets_array = jsonObject["sets"].toArray();


    // For every object in the array, check if its start and end are the target.
    // If so return it.

    for(const auto& set_obj: sets_array) {
        QJsonObject set = set_obj.toObject();
        if(set["start"].toString() == start && set["end"].toString() == end) {
            return set;
        }
    }

  // If there are no matches, return an empty object.
  return QJsonObject{};
}

Now you can search by (start, end) tuple--what a great feeling. By grabbing this entire object, you have all the information about it: the start, the end, the id, and all the units.

Now you just need to create a function that uses this helper to index into the units array appropriately.

QJsonObject get_kth_unit_by_start_and_end(const QJsonObject& obj, 
                                          const QString& start, 
                                          const QString& end,
                                          const std::size_t k) {
    // get the target by start and end tuple
    const QJsonObject target_obj = find_by_start_and_end(obj, start, end);

    // If the target is empty, the `units` array will not exist.
    if(target_obj.empty())
        return target_obj;

    // get the units array from that returned target
    QJsonArray units = target_obj["units"].toArray();

    if(units.size() <= k) {
       throw std::runtime_exception("Invalid k.");
    }

    // return the kth unit object.
    return units[k].toObject();
}

I haven't tested this; however, this should give you a good start.

units_array.append(set_loopObj["units"].toArray());

int idy = 0;
for (const QJsonValue& unit_val: units_array)
{
    QJsonObject unit_loopObj = unit_val.toObject();

When using append on units_array , you add the whole value passed as argument as a single element at the end of the array. Your for-loop is then filling unit_val with the current element, so the whole array, which you try to read as an object.

I think what you wanted to achieve is:

units_array = set_loopObj["units"].toArray();

int idy = 0;
for (const QJsonValue& unit_val: units_array)
{
    QJsonObject unit_loopObj = unit_val.toObject();

Here you can just remove units_array and write:

int idy = 0;
for (const QJsonValue& unit_val: set_loopObj["units"].toArray())
{
    QJsonObject unit_loopObj = unit_val.toObject();

You can do the same for sets_array and write:

int idx = 0;
for (const QJsonValue& set_val: jsonObject["sets"].toArray())
{
    QJsonObject set_loopObj = set_val.toObject();

About storing the data, first declare structures to represent your data:

struct MyUnit
{
    QString id;
    QString name;
    int position;
    int capacity;
};

struct MySet
{
    QString id;
    QString start;
    QString end;
    QVector<MyUnit> units;
};

You can then use something like that:

QMap<QPair<QString, QString>, MySet> data;

for (const QJsonValue& set_val: jsonObject["sets"].toArray())
{
    QJsonObject set_loopObj = set_val.toObject();

    // parse id, start, and end

    QVector<MyUnit> units;

    for (const QJsonValue& unit_val: set_loopObj["units"].toArray())
    {
        QJsonObject unit_loopObj = unit_val.toObject();

        // parse id, name, position, and capacity

        units.append(MyUnit{id, name, position, capacity});
    }

    data.insert(qMakePair(start, end), MySet{id, start, end, units});
}

And use it:

MySet at_set = data.value(qMakePair("Alpha", "Theta"));

qDebug() << at_set.id;

for(MyUnit at_unit: at_set.units)
{
    // use at_unit
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM