Pedro Vicente
Pedro Vicente

Reputation: 749

nlohmann::json array of objects conversion into memory structure

I am using the nlohmann JSON C++ library to read a JSON file with this structure:

{
  "Main": [
    {
      "obj1": "bar"
    },
    {
      "obj2": "foo"
    }
  ]
}

The main object with key known to the user "Main" contains an array of objects with unknown key names.

I want to transfer the JSON object in the following program to a C++ structure. How could this be done?

#include <nlohmann/json.hpp>
#include <iostream>
#include <fstream>
using json = nlohmann::json;

int main()
{
  std::ifstream ifs("../test/test_2.json");
  json js = json::parse(ifs);

  for (json::iterator it = js.begin(); it != js.end(); ++it) 
  {
    std::cout << it.key() << " :\n";
    std::cout << it.value() << "\n";
  }

  if (js.contains("Main"))
  {
    json a = js["Main"];
    for (size_t idx = 0; idx < a.size(); ++idx)
    {
      json o = a.at(idx);
      std::cout << o << "\n";
    }
  }
  return 0;
}

Output:

Main :
[{"obj1":"bar"},{"obj2":"foo"}]
{"obj1":"bar"}
{"obj2":"foo"}

Upvotes: 1

Views: 7878

Answers (3)

Pedro Vicente
Pedro Vicente

Reputation: 749

This is one way to read and write the JSON in the original format, versions of the functions from_json() and to_json() are needed:

#include <iostream>
#include <fstream>
#include <map>
#include <nlohmann/json.hpp>
#include <string>
#include <vector>
using json = nlohmann::json;

struct key_value_t
{
  std::string key;
  std::string value;
};

void from_json(const json& j, key_value_t& kv)
{
  assert(j.is_object());
  assert(j.size() == 1);
  kv.key = j.begin().key();
  kv.value = j.begin().value();
}

void to_json(json& j, const key_value_t& kv)
{
  j = nlohmann::json{
    {kv.key, kv.value}
  };
}

int main()
{
  try
  {
    std::string str{ R"({ "Main": [ { "obj1": "bar" }, { "obj2": "foo" } ] })" };
    json js_in = json::parse(str);

    std::vector<key_value_t> kv_in;

     //get input object
    js_in.at("Main").get_to(kv_in);

    if (js_in.contains("Main"))
    {
      nlohmann::json js_out = nlohmann::json::object_t();

      std::vector<key_value_t> kv_;
      key_value_t kv1{ "obj1", "1" };
      key_value_t kv2{ "obj2", "2" };
      kv_.push_back(kv1);
      kv_.push_back(kv2);

      nlohmann::json js_arr = kv_;

      //add to main JSON object
      js_out += {"Main", js_arr};

      //save output
      std::ofstream ofs("output.json");
      ofs << std::setw(2) << js_out << std::endl;
      ofs.close();

    }
  }

  catch (std::exception& e)
  {
    std::cout << e.what() << '\n';
  }
  return 0;
}

Output is:

{
  "Main": [
    {
      "obj1": "1"
    },
    {
      "obj2": "2"
    }
  ]
}

Upvotes: 0

Pedro Vicente
Pedro Vicente

Reputation: 749

Yes, but the API gives an automatic way to do this by means of NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE; it seems it would be like just defining a vector like this, but this program gives the error

[json.exception.out_of_range.403] key 'key' not found

program

#include <nlohmann/json.hpp>
#include <iostream>
#include <fstream>
using json = nlohmann::json;

struct key_value_t
{
  std::string key;
  std::string value;
};
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(key_value_t, key, value);

int main()
{
  try
  {
    std::string str{ R"({ "Main": [ { "obj1": "bar" }, { "obj2": "foo" } ] })" };
    json js = json::parse(str);

    std::vector<key_value_t> kv;
    if (js.contains("Main"))
    {
      js.at("Main").get_to(kv);
    }
  }

  catch (std::exception& e)
  {
    std::cout << e.what() << '\n';
  }
  return 0;
}

Upvotes: 0

rturrado
rturrado

Reputation: 8054

You could parse the vector of maps under Main with:

auto objects{ j.at("Main").get<objects_t>() };

Where:

using object_t = std::map<std::string, std::string>;
using objects_t = std::vector<object_t>;

[Demo]

#include <fmt/ranges.h>
#include <iostream>  // cout
#include <map>
#include <nlohmann/json.hpp>
#include <string>
#include <vector>

using json = nlohmann::json;

using object_t = std::map<std::string, std::string>;
using objects_t = std::vector<object_t>;

int main() {
    std::string str{R"({ "Main": [ { "obj1": "bar" }, { "obj2": "foo" } ] })"};
    json j = json::parse(str);
    auto objects{ j.at("Main").get<objects_t>() };
    fmt::print("{}", objects);
}

// Outputs:
//
//   [{"obj1": "bar"}, {"obj2": "foo"}]

Upvotes: 3

Related Questions