Reputation: 85
i have a JSON file:
{
"level_1" :
{
"level_1_1" : [[1, "text", 0, 1, 2],
[2, "text", 1, 3, 4],
[3, "text", 5, 6, 8],
[7, "text", 5, 4, 3]
],
"level_1_2" : [[....]],
"level_1_3" : [[....]]
}
}
I want to get content get_child(level_1.level_1_1)
and put it in std::map<int, struct json_data>
, where
struct json_data {
std::string str;
int num1, num2, num3;
};
How can I do this?
Upvotes: 2
Views: 711
Reputation: 393164
Please don't use Property Tree as if it were a JSON library.
In boost 1.75 you can use Boost Json:
Boost Json allows you to have automatic conversions using value_to<T>
. Extend it for your own type with tag_invoke
:
struct json_data {
std::string str;
int64_t num1, num2, num3;
//for json input
friend json_data tag_invoke(json::value_to_tag<json_data>, json::value const& v) {
auto& arr = v.as_array();
return {
value_to<std::string>(arr[1]),
//std::string(s.data(), s.size()),
arr[2].as_int64(),
arr[3].as_int64(),
arr[4].as_int64()
};
}
};
Now you can "just" read it like:
auto doc = json::parse(json_text);
using SubLevel = std::vector<json_data>;
using Level = std::map<std::string, SubLevel>;
auto level_1 = json::value_to<Level>(doc.as_object()["level_1"]);
It just works. You can take it a step further and parse the whole file:
using File = std::map<std::string, Level>;
auto all = json::value_to<File>(doc);
fmt::print("Whole file: {}\n", fmt::join(all, "\n"));
Live On Compiler Explorer
#include <boost/json.hpp>
#include <boost/json/src.hpp> // for header only
#include <iostream>
#include <iomanip>
#include <fmt/ranges.h>
#include <fmt/ostream.h>
namespace json = boost::json;
struct json_data {
std::string str;
int64_t num1, num2, num3;
//for json input
friend json_data tag_invoke(json::value_to_tag<json_data>, json::value const& v) {
auto& arr = v.as_array();
return {
value_to<std::string>(arr[1]),
//std::string(s.data(), s.size()),
arr[2].as_int64(),
arr[3].as_int64(),
arr[4].as_int64()
};
}
//for debug output
friend std::ostream& operator<<(std::ostream& os, json_data const& jd) {
return os << '{' << std::quoted(jd.str) << ';' << jd.num1 << ';'
<< jd.num2 << ';' << jd.num3 << '}';
}
};
extern std::string const json_text;
int main() {
auto doc = json::parse(json_text);
using SubLevel = std::vector<json_data>;
using Level = std::map<std::string, SubLevel>;
auto level_1 = json::value_to<Level>(doc.as_object()["level_1"]);
fmt::print("Level 1: {}\n", fmt::join(level_1, "\n\t - "));
using File = std::map<std::string, Level>;
auto all = json::value_to<File>(doc);
fmt::print("Whole file: {}\n", all);
}
std::string const json_text = R"(
{
"level_1": {
"level_1_1": [
[1, "text", 0, 1, 2],
[2, "text", 1, 3, 4],
[3, "text", 5, 6, 8],
[7, "text", 5, 4, 3]],
"level_1_2": [
[9, "text", 8, 9, 10],
[10, "text", 9, 11, 12],
[11, "text", 13, 14, 16],
[15, "text", 13, 12, 11]],
"level_1_3": [
[17, "text", 16, 17, 18],
[18, "text", 17, 19, 20],
[19, "text", 21, 22, 24],
[23, "text", 21, 20, 19]]
}
})";
Prints
Level 1: ("level_1_1", {{"text";0;1;2}, {"text";1;3;4}, {"text";5;6;8}, {"text";5;4;3}})
- ("level_1_2", {{"text";8;9;10}, {"text";9;11;12}, {"text";13;14;16}, {"text";13;12;
11}})
- ("level_1_3", {{"text";16;17;18}, {"text";17;19;20}, {"text";21;22;24}, {"text";21;
20;19}})
Whole file: ("level_1", {("level_1_1", {{"text";0;1;2}, {"text";1;3;4}, {"text";5;6;8}, {"text
";5;4;3}}), ("level_1_2", {{"text";8;9;10}, {"text";9;11;12}, {"text";13;14;16}, {"text";13;12
;11}}), ("level_1_3", {{"text";16;17;18}, {"text";17;19;20}, {"text";21;22;24}, {"text";21;20;
19}})})
If you must, you can try to get close using Boost Property Tree:
#define BOOST_BIND_GLOBAL_PLACEHOLDERS
#include <boost/property_tree/json_parser.hpp>
#include <iostream>
#include <sstream>
#include <iomanip>
#include <fmt/ranges.h>
#include <fmt/ostream.h>
using boost::property_tree::ptree;
struct json_data {
std::string str;
int num1, num2, num3;
friend void read_tree(ptree const& pt, std::vector<json_data>& into) {
auto r = pt.equal_range("");
for (;r.first != r.second; ++r.first) {
read_tree(r.first->second, into.emplace_back());
}
}
friend void read_tree(ptree const& pt, json_data& into) {
auto r = pt.equal_range("");
assert(std::distance(r.first, r.second) == 5);
auto it = r.first;
into = {
(++it)->second.get_value<std::string>(),
//std::string(s.data(), s.size()),
(++it)->second.get_value<int>(),
(++it)->second.get_value<int>(),
(++it)->second.get_value<int>()
};
}
//for debug output
friend std::ostream& operator<<(std::ostream& os, json_data const& jd) {
return os << '{' << std::quoted(jd.str) << ';' << jd.num1 << ';'
<< jd.num2 << ';' << jd.num3 << '}';
}
};
extern std::string const json_text;
int main() {
ptree pt;
{
std::istringstream iss(json_text);
read_json(iss, pt);
}
std::vector<json_data> level_1;
read_tree(pt.get_child("level_1.level_1_2"), level_1);
fmt::print("level_1: {}\n", fmt::join(level_1, "\n\t - "));
}
std::string const json_text = R"(
{
"level_1": {
"level_1_1": [
[1, "text", 0, 1, 2],
[2, "text", 1, 3, 4],
[3, "text", 5, 6, 8],
[7, "text", 5, 4, 3]],
"level_1_2": [
[9, "text", 8, 9, 10],
[10, "text", 9, 11, 12],
[11, "text", 13, 14, 16],
[15, "text", 13, 12, 11]],
"level_1_3": [
[17, "text", 16, 17, 18],
[18, "text", 17, 19, 20],
[19, "text", 21, 22, 24],
[23, "text", 21, 20, 19]]
}
})";
Prints
level_1: {"text";8;9;10}
- {"text";9;11;12}
- {"text";13;14;16}
- {"text";13;12;11}
Prints
Level 1:
- {"text";8;9;10}
- {"text";9;11;12}
- {"text";13;14;16}
- {"text";13;12;11}
Upvotes: 1