creating nested json object in c++ using nlohmann json - c++

I am working with https://github.com/nlohmann/json and it works well. However I am finding difficulties to create the following json outout
{
"Id": 1,
"Child": [
{
"Id": 2
},
{
"Id": 3,
"Child": [
{
"Id" : 5
},
{
"Id" : 6
}
]
},
{
"Id": 4
}
]
}
Every node must have an id and an array ("Child" element). Any child can recursively continue to have Id or Child. The json above is just an example. What I want is to create a chain between father and children node using nlohmann json.
Numbers 1, 2, 3, .... are picked up randomly. We don't care for now about those values.
Any idea how to create it?
Code so far
#include <iostream>
#include <string>
#include <vector>
#include "json.hpp"
using json = nlohmann::json;
struct json_node_t {
int id;
std::vector<json_node_t> child;
};
int main( int argc, char** argv) {
json j;
for( int i = 0; i < 3; i++) {
json_node_t n;
n.id = i;
j["id"] = i;
if ( i < 2 ) {
j["child"].push_back(n);
}
}
return 0;
}

In order to serialize your own type, you need to implement a to_json function for that type.
#include <iostream>
#include <string>
#include <vector>
#include "json.hpp"
using namespace std;
using json = nlohmann::json;
struct json_node_t {
int id;
std::vector<json_node_t> child;
};
void to_json(json& j, const json_node_t& node) {
j = {{"ID", node.id}};
if (!node.child.empty())
j.push_back({"children", node.child});
}
int main() {
json_node_t node = {1, {{2, {}}, {3, {{5, {}}, {6, {}}}}, {4, {}}}};
json j = node;
cout << j.dump(2) << endl;
return 0;
}
Output:
{
"ID": 1,
"children": [
{
"ID": 2
},
{
"ID": 3,
"children": [
{
"ID": 5
},
{
"ID": 6
}
]
},
{
"ID": 4
}
]
}
A couple of more ways to initialize json_node_t (all producing the same tree and the same output):
struct json_node_t {
int id;
std::vector<json_node_t> child;
json_node_t(int node_id, initializer_list<json_node_t> node_children = initializer_list<json_node_t>());
json_node_t& add(const json_node_t& node);
json_node_t& add(const initializer_list<json_node_t>& nodes);
};
json_node_t::json_node_t(int node_id, initializer_list<json_node_t> node_children) : id(node_id), child(node_children) {
}
json_node_t& json_node_t::add(const json_node_t& node) {
child.push_back(node);
return child.back();
}
json_node_t& json_node_t::add(const initializer_list<json_node_t>& nodes) {
child.insert(child.end(), nodes);
return child.back();
}
int main() {
json_node_t node_a = {1, {{2, {}}, {3, {{5, {}}, {6, {}}}}, {4, {}}}};
json_node_t node_b = {1, {2, {3, {5, 6}}, 4}};
json_node_t node_c(1);
node_c.add(2);
node_c.add(3).add({5, 6});
node_c.add(4);
cout << json(node_a).dump(2) << endl << endl;
cout << json(node_b).dump(2) << endl << endl;
cout << json(node_c).dump(2) << endl;
return 0;
}

Related

How create a JSON-file from my data? (with ะก++, boost json)

I wanted to create a Json-file that will be created from the data received earlier. I don't understand how to work with Json files at all. I want to use the Boost library, because I am using it in another part of this program. I need to create a Json-file with a specific structure which I have attached below.
I need to get JSON:
{
"track": {
"Wheels": {
"Wheel": [
{
"start_pos": "10",
"end_pos": "25"
},
{
"start_pos": "22",
"end_pos": "78"
}
]
},
"Brakes": {
"Brake": [
{
"start_pos": "10",
"midl_pos": "25"
}
]
}
}
}
C++:
#include "boost/property_tree/ptree.hpp"
#include "boost/property_tree/json_parser.hpp"
#include <string>
using namespace std;
using boost::property_tree::ptree;
struct wheel
{
string start_pos;
string end_pos;
};
struct brake
{
string start_pos;
string midl_pos;
};
int main()
{
string tr = "track";
string ws = "Wheels";
string bs = "Brakes";
struct wheel w1;
w1.start_pos = "10";
w1.end_pos = "25";
struct wheel w2;
w2.start_pos = "22";
w2.end_pos = "78";
struct brake b1;
b1.start_pos = "10";
b1.midl_pos = "25";
return 0;
}
Implementing it with the Boost JSON customization points.
Doing it test-driven:
Live On Coliru
#include "boost/json/src.hpp" // header-only approach
#include <iostream>
namespace json = boost::json;
using json::value_from;
using json::value_to;
static const json::value expected = json::parse(R"({
"track": {
"Wheels": {
"Wheel": [
{
"start_pos": "10",
"end_pos": "25"
},
{
"start_pos": "22",
"end_pos": "78"
}
]
},
"Brakes": {
"Brake": [
{
"start_pos": "10",
"midl_pos": "25"
}
]
}
}
})");
namespace MyLib {
struct wheel { int start_pos, end_pos; };
struct brake { int start_pos, midl_pos; };
struct track {
std::vector<wheel> wheels;
std::vector<brake> brakes;
};
void tag_invoke(json::value_from_tag, json::value& jv, wheel const& w) {
jv = {
{"start_pos", std::to_string(w.start_pos)},
{"end_pos", std::to_string(w.end_pos)},
};
}
void tag_invoke(json::value_from_tag, json::value& jv, brake const& b) {
jv = {
{"start_pos", std::to_string(b.start_pos)},
{"midl_pos", std::to_string(b.midl_pos)},
};
}
void tag_invoke(json::value_from_tag, json::value& jv, track const& t) {
jv = {{"track",
{
{"Wheels", {{"Wheel", t.wheels}}},
{"Brakes", {{"Brake", t.brakes}}},
}}};
}
}
int main()
{
MyLib::track track{{
{10, 25},
{22, 78},
},
{
{10, 25},
}};
json::value output = json::value_from(track);
std::cout << output << "\n";
std::cout << expected << "\n";
std::cout << "matching: " << std::boolalpha << (output == expected) << "\n";
}
Prints
{"track":{"Wheels":{"Wheel":[{"start_pos":"10","end_pos":"25"},{"start_pos":"22","end_pos":"78"}]},"Brakes":{"Brake":[{"start_pos":"10","midl_pos":"25"}]}}}
{"track":{"Wheels":{"Wheel":[{"start_pos":"10","end_pos":"25"},{"start_pos":"22","end_pos":"78"}]},"Brakes":{"Brake":[{"start_pos":"10","midl_pos":"25"}]}}}
matching: true
BONUS
Adding full round-trip support. I elected to remove the to_string as it looks like it may have been a requirement from the Boost Property Tree limitations only:
Live On Coliru
#include "boost/json/src.hpp" // header-only approach
#include <iostream>
namespace json = boost::json;
using json::value_from;
using json::value_to;
namespace MyLib {
struct wheel {
int start_pos, end_pos;
bool operator==(wheel const&) const = default;
};
struct brake {
int start_pos, midl_pos;
bool operator==(brake const&) const = default;
};
struct track {
std::vector<wheel> wheels;
std::vector<brake> brakes;
bool operator==(track const&) const = default;
};
void tag_invoke(json::value_from_tag, json::value& jv, wheel const& w) {
jv = {{"start_pos", w.start_pos}, {"end_pos", w.end_pos}};
}
void tag_invoke(json::value_from_tag, json::value& jv, brake const& b) {
jv = {{"start_pos", b.start_pos}, {"midl_pos", b.midl_pos}};
}
void tag_invoke(json::value_from_tag, json::value& jv, track const& t) {
jv = {{"track",
{
{"Wheels", {{"Wheel", t.wheels}}},
{"Brakes", {{"Brake", t.brakes}}},
}}};
}
wheel tag_invoke(json::value_to_tag<wheel>, json::value const& jv) {
return {
value_to<int>(jv.at("start_pos")),
value_to<int>(jv.at("end_pos")),
};
}
brake tag_invoke(json::value_to_tag<brake>, json::value const& jv) {
return {
value_to<int>(jv.at("start_pos")),
value_to<int>(jv.at("midl_pos")),
};
}
track tag_invoke(json::value_to_tag<track>, json::value const& jv) {
auto& track = jv.at("track");
return {
value_to<decltype(track::wheels)>(track.at("Wheels").at("Wheel")),
value_to<decltype(track::brakes)>(track.at("Brakes").at("Brake")),
};
}
}
int main()
{
MyLib::track const track{{
{110, 125},
{111, 126},
{142, 198},
},
{
{10, 25},
{120, 135},
}};
json::value output = json::value_from(track);
std::cout << output << "\n";
std::cout << "Roundtrip: " << std::boolalpha
<< (value_to<MyLib::track>(output) == track) << "\n";
}
Prints
{"track":{"Wheels":{"Wheel":[{"start_pos":110,"end_pos":125},{"start_pos":111,"end_pos":126},{"start_pos":142,"end_pos":198}]},"Brakes":{"Brake":[{"start_pos":10,"midl_pos":25},{"start_pos":120,"midl_pos":135}]}}}
Roundtrip: true
Despite comments suggesting to use a different library, which they could, I think #Nindzzya wanted to specifically use the boost library.
Using the boost library from How to use boost::property_tree to load and write JSON:
#include "boost/property_tree/ptree.hpp"
#include "boost/property_tree/json_parser.hpp"
#include <string>
#include <iostream>
using namespace std;
namespace pt = boost::property_tree;
struct wheel
{
string start_pos;
string end_pos;
};
struct brake
{
string start_pos;
string midl_pos;
};
int main()
{
string tr = "track";
string ws = "Wheels";
string bs = "Brakes";
struct wheel w1;
w1.start_pos = "10";
w1.end_pos = "25";
struct wheel w2;
w2.start_pos = "22";
w2.end_pos = "78";
struct brake b1;
b1.start_pos = "10";
b1.midl_pos = "25";
pt::ptree wheel1, wheel2, wheels, wheel;
pt::ptree brake1, brakes, brake;
pt::ptree track, root;
wheel1.put("start_pos", w1.start_pos);
wheel1.put("end_pos", w1.end_pos);
wheel2.put("start_pos", w2.start_pos);
wheel2.put("end_pos", w2.end_pos);
wheels.push_back(make_pair("", wheel1));
wheels.push_back(make_pair("", wheel2));
wheel.add_child("Wheel", wheels);
track.add_child(ws, wheel);
brake1.put("start_pos", b1.start_pos);
brake1.put("midl_pos", b1.midl_pos);
brakes.push_back(make_pair("", brake1));
brake.add_child("Brake", brakes);
track.add_child(bs, brake);
root.add_child(tr, track);
pt::write_json(std::cout, root);
return 0;
}
results in:
{
"track": {
"Wheels": {
"Wheel": [
{
"start_pos": "10",
"end_pos": "25"
},
{
"start_pos": "22",
"end_pos": "78"
}
]
},
"Brakes": {
"Brake": [
{
"start_pos": "10",
"midl_pos": "25"
}
]
}
}
}

Get Disjunctive Support for itemset?

Disjunctive Support :
let an itemset I formed by any non-empty subset from C
Supp(I) is the number of transactions containing at least one item of I for example i have :
vector < vector <int> > transactions = {{1, 2},
{2, 3, 7},
{4,6},
{1,5,8}};
vector<int> I ={1,2};
expected result :
Supp(I) = 3
but my code return Supp(I) = 1
#include <iostream>
#include <vector>
using namespace std;
int getSupport(vector < vector<int> > &transactions, vector<int> item) {
int ret = 0;
for(auto&row:transactions){
int i, j;
if(row.size() < item.size()) continue;
for(i=0, j=0; i < row.size();i++) {
if(j==item.size()) break;
if(row[i] == item[j]) j++;
}
if(j==item.size()){
ret++;
}
}
return ret;
}
int main() {
vector < vector <int> > transactions = {{1, 2},
{2, 3, 7},
{4,6},
{1,5,8}};
vector <int> I={1,2};
int D = getSupport(transactions, I);
printf("Disjunctive support = %d",D);
return 0;
}
You wrote that:
Supp(I) is the number of transactions containing at least one item of
I
But your implementation looks like you are trying to count transactions containing all the items of I.
Anyway if you still need implementation for the defintion you supplied, you can try this:
#include <iostream>
#include <vector>
int getSupport(std::vector<std::vector<int>> const & transactions, std::vector<int> const & item) {
int ret = 0;
for (auto const & tran : transactions) {
bool bFoundAtLeastOne{ false };
for (auto const & tran_elem : tran) {
for (auto const & item_elem : item)
{
if (tran_elem == item_elem)
{
ret++;
bFoundAtLeastOne = true;
break;
}
}
if (bFoundAtLeastOne) {
break;
}
}
}
return ret;
}
int main() {
std::vector<std::vector<int>> transactions =
{ { 1, 2 },
{ 2, 3, 7 },
{ 4,6 },
{ 1,5,8 } };
std::vector<int> I = { 1,2 };
int D = getSupport(transactions, I);
printf("Disjunctive support = %d\n", D);
return 0;
}
Some notes:
Better to avoid using namespace std - see here Why is "using namespace std;" considered bad practice?
I changed passing all the vectors by const& for efficiency and safety.

Initialize multiple dimension array

How can I initialize array with variables like int x[row][col];
int myArray[7][4] = { {1,2,3,4}, {5,6,7,8}, {5,6,7,8}, {5,6,7,8}, {5,6,7,8}, {5,6,7,8}, {5,6,7,8} };
i want to initialize array as this =
int myarray({2,3,4},{12,4,5},{2,2,2})
The exact answer is you cannot initialize an array like that, i.e., without providing both row and col at compile time, though std::vector can do the job for you.
You can use some code like this:
#include <iostream>
#include <vector>
void print_vector(std::vector<int> &v) {
std::cout << "{ ";
for (auto &&i : v) {
std::cout << i;
if (&i != &v.back()) {
std::cout << ",";
}
}
std::cout << " }";
}
void print_matrix(std::vector<std::vector<int>> &v) {
std::cout << "{ ";
for (auto &&i : v) {
print_vector(i);
if (&i != &v.back()) {
std::cout << ", ";
}
}
std::cout << " }" << std::endl;
}
int main() {
std::vector<std::vector<int>> v = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}};
// same as std::vector<std::vector<int>> v({{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}});
print_matrix(v);
// prints { { 1,2,3,4 }, { 5,6,7,8 }, { 9,10,11,12 } } on stdout
}
I have included print_vector and print_matrix since the OP asked about them in the comments, though without thinking much about them. You can get better implementations on this thread.

how to order map by vector value c++

std::map<int, std::vector<int>> tmp_map = { { 1, [10,5,4] }, { 2, [5,5,1] },
{ 3, [2,4,3] }, { 4, [9,7,8] } };
I want to order this map by the 3rd value in vector value.
So the outcome will be like :
{ { 2, [5,5,1] },{ 3, [2,4,3] },{ 1, [10,5,4] },{ 4, [9,7,8] } }
Standard approach . . .
Copy map to vector
Sort vector with custom comparator
#include <iostream>
#include <map>
#include <vector>
#include <utility>
#include <algorithm>
int main() {
std::map<int, std::vector<int>> tmp_map = { { 1,{10,5,4} }, { 2,{5,5,1} },
{ 3,{2,4,3} }, { 4,{9,7,8} } };
// For easier and shorter writing
using DataType = std::pair<int, std::vector<int>>;
// Create Vector with Elements from Map
std::vector<DataType> data(tmp_map.begin(), tmp_map.end());
// Sort data
std::sort(data.begin(), data.end(), [](const DataType& d1, const DataType& d2) { return d1.second[2] < d2.second[2]; });
// show result
for (const auto& [key, value] : data) {
std::cout << key << " --> ";
for (const int i : value) std::cout << i << " ";
std::cout << "\n";
}
return 0;
}
You're map is already sorted by its key value so you cannot reorder it inplace. What you should do instead is copy it into a vector and then sort it using a custom operator like this:
#include <map>
#include <vector>
#include <algorithm>
int main()
{
std::map<int, std::vector<int>> tmp_map = { { 1, {10,5,4} }, { 2, {5,5,1} },
{ 3, {2,4,3} }, { 4, {9,7,8} } };
//! Copy the map
using P = std::pair<int, std::vector<int>>;
std::vector<P> copy(tmp_map.begin(), tmp_map.end());
//! Sort it the way you want (here I'm sorting on based on the second element
//! of the array.
std::sort(copy.begin(), copy.end(), [](const P& a, const P& b)
{
return a.second[2] < b.second[2];
});
}

Is there any way to add parameter from json file to vector<ObjClass>?

I have a class and file "person.json". Is there any way to read that file to Data Structure like vector?
my file:
{
"students": [
{
"name": "Jack",
"year": 1,
"grade": 6.95
},
{
"name": "Paul",
"year": 2,
"grade": 8.54
},
{
"name": "John",
"year": 3,
"grade": 9.49
},
{
"name": "Annie",
"year": 1,
"grade": 3.12
}
]
}
I would like to set person name, person year and person grade from that file, not like in my main class
my main class :
int main() {
ifstream datafile("people.json");
json j;
datafile >> j;
cout << j << endl;
datafile.close();
vector<PersonClass> list;
string name = "Jonas";
float year = 420;
float grade = 69;
PersonClass *f1;
f1 = new PersonClass;
f1->set_name(name);
f1->set_year(year);
f1->set_grade(grade);
list.push_back(*f1);
}
As PaulMcKenzie mentioned in the comments, get a JSON library geared towards C++. jsoncons, nlohmann, and ThorsSerializer all support conversion between JSON and C++ data structures, see How To Convert Vector To Json Object? C++ and C++ JSON Serialization. Regarding your specific question, below is a non-intrusive way of doing it in jsoncons.
#include <jsoncons/json.hpp>
class Person
{
std::string name_;
size_t year_;
double grade_;
public:
const std::string& get_name() const
{
return name_;
}
void set_name(const std::string& value)
{
name_ = value;
}
size_t get_year() const
{
return year_;
}
void set_year(size_t value)
{
year_ = value;
}
double get_grade() const
{
return grade_;
}
void set_grade(double value)
{
grade_ = value;
}
};
class Students
{
std::vector<Person> students_;
public:
Students(const std::vector<Person>& students)
{
students_ = students;
}
const std::vector<Person>& get_students() const { return students_; }
};
JSONCONS_ALL_GETTER_SETTER_NAME_TRAITS(Person, (get_name,set_name,"name"),
(get_year,set_year, "year"),
(get_grade, set_grade, "grade"))
JSONCONS_ALL_CTOR_GETTER_NAME_TRAITS(Students, (get_students, "students"))
const std::string input = R"(
{
"students": [
{
"name": "Jack",
"year" : 1,
"grade" : 6.95
},
{
"name": "Paul",
"year" : 2,
"grade" : 8.54
},
{
"name": "John",
"year" : 3,
"grade" : 9.49
},
{
"name": "Annie",
"year" : 1,
"grade" : 3.12
}
]
}
)";
int main()
{
std::istringstream is(input);
Students result = jsoncons::decode_json<Students>(is);
std::cout << "(1)\n";
for (const auto& person : result.get_students())
{
std::cout << person.get_name() << ", " << person.get_year() << ", " << person.get_grade() << "\n";
}
std::cout << "\n";
std::ostringstream os;
jsoncons::encode_json(result, os, jsoncons::indenting::indent);
std::cout << "(2)\n";
std::cout << os.str() << "\n";
}
Output:
(1)
Jack, 1, 6.95
Paul, 2, 8.54
John, 3, 9.49
Annie, 1, 3.12
(2)
{
"students": [
{
"grade": 6.95,
"name": "Jack",
"year": 1
},
{
"grade": 8.54,
"name": "Paul",
"year": 2
},
{
"grade": 9.49,
"name": "John",
"year": 3
},
{
"grade": 3.12,
"name": "Annie",
"year": 1
}
]
}