1

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"}
4
  • Do you have a certain "C++ structure" in mind? Something other than your object js, I assume? Commented Feb 24, 2022 at 21:35
  • because the array objects are 2 strings, this seems obvious struct key_value_t { std::string key; std::string value; }; Commented Feb 24, 2022 at 21:39
  • 2
    With such a class, key_value_t my_new_structure{ it.key(), it.value() }; would work. Commented Feb 24, 2022 at 21:41
  • yes, this works, kind of obvious for (json::iterator it = o.begin(); it != o.end(); ++it) { std::cout << it.key() << " :\n"; std::cout << it.value() << "\n"; key_value_t my_new_structure{ it.key(), it.value() }; } Commented Feb 24, 2022 at 21:54

3 Answers 3

3

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"}]
Sign up to request clarification or add additional context in comments.

Comments

0

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;
}

2 Comments

I think that, now, you're telling the JSON parser that the input JSON is a vector of structs with a field key and a field value. So the input JSON you would need is something like: R"({ "Main": [ { "key": "obj1", "value": "bar" }, { "key": "obj2", "value": "foo" } ] })". Check it working with that change here: godbolt.org/z/jzr4Yv3Wd.
Notice that, in your original post, { "obj1" : "bar" } and { "obj2" : "foo" } can be mapped directly to an object of type std::map<std::string, std::string>.
0

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"
    }
  ]
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.