nlohmann::json array of objects conversion into memory structure - c++

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

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

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

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

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

C++ std::any how to check if type of std::any is vector

#include <iostream>
#include <vector>
#include <string>
#include <any>
#include <map>
#include <functional>
#include <exception>
using namespace std;
using MapAny = std::map<string, any>;
int square(int x) {
return x*x;
}
vector<int> parse(map<string, vector<MapAny>> mapping)
{
vector<MapAny> func_square = mapping["square"];
vector<int> res;
for (const auto &mapany : func_square) {
try {
int x = any_cast<int>(mapany.at("x"));
res.push_back(square(x));
}
catch (exception e) {
vector<int> xs = any_cast<vector<int>>(mapany.at("x"));
for (int x : xs) res.push_back(square(x));
}
}
return res;
}
int main()
{
map<string, vector<MapAny>> function_map_value, function_map_array;
function_map_value = {
{"square", { {{"x", 5}}, {{"x", 10}} }}
};
vector<MapAny> vec;
vec.push_back({{"x", vector<int>({5, 10}) }});
function_map_array = {
{"square", vec}
};
vector<int> res1 = parse(function_map_value);
vector<int> res2 = parse(function_map_array);
for (int i=0; i<res1.size(); i++) cout << res1[i] << " "; cout << "\n";
for (int i=0; i<res2.size(); i++) cout << res2[i] << " "; cout << "\n";
return 0;
}
I'm trying to make a function parser that can accept any type, such as both scalar and vector values, like in Python dict().
However, I'm not sure how to check if a std::any object has type std::vector. In the code above, if any_cast fails, it will throw exception and I know it's a std::vector. It's ugly and rely on throwing exception as an expected behavior.
How can I change the above code into something like:
if (is_vector(mapany.at("x")) {
// deal with vector
}
else {
// deal with scalar
}

C++ Design a struct of the data

I have a data struct -
{"BRAZIL", { {"IPHONE",{100,100} },{"IPOD", {65,100} } }
I want to use operator = to save the data into a struct . I tried to design the struct like map<string, vector<map<string, vector<int>>>>,and use -
price_stock["BRAZIL"] = { { "IPHONE", { 100, 100 } }, { "IPOD", { 65, 100 } } }
But I failed to directly assign the data into struct.
You are missing a level of braces:
price_stock["BRAZIL"] = {
{ // first element in vector
{"IPHONE", {100, 100}} // element in inner map
},
{ // second element in vector
{"IPOD", {65, 100}} // element in inner map
}
};
Below I have given a simple example on how to use a struct in C++.
#include <iostream>
#include <string>
#include <map>
#include <vector>
struct prices {
std::string title;
int stock;
int price;
};
int main ()
{
std::map<std::string, std::vector<prices> > priceMap;
std::vector<prices> priceVec {{"IPHONE",100,100}, {"IPOD",65,100}};
priceMap["Brazil"] = priceVec;
for (auto& itemVector : priceMap) {
for (auto& structItem : itemVector.second) {
std::cout << structItem.title << ":" << structItem.price << std::endl;
}
}
}

How to use count() function in a vector<struct> according to specific criteria

I have a vector of type struct with some elements, and trying to count the number of occurrences of an element(value) in its corresponding column of the vector. I know how to count on a simple vector, e.g on vector of type string. But am stuck on vector<struct>. Any possible solution or suggestion?
Sample code:
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
struct my_struct
{
std::string first_name;
std::string last_name;
};
int main()
{
std::vector<my_struct> my_vector(5);
my_vector[0].first_name = "David";
my_vector[0].last_name = "Andriw";
my_vector[1].first_name = "Jhon";
my_vector[1].last_name = "Monta";
my_vector[2].first_name = "Jams";
my_vector[2].last_name = "Ruth";
my_vector[3].first_name = "David";
my_vector[3].last_name = "AAA";
my_vector[4].first_name = "Jhon";
my_vector[4].last_name = "BBB";
for(int i = 0; i < my_vector.size(); i++)
{
int my_count=count(my_vector.begin(), my_vector.end(),my_vector[i].first_name);
/*I need help to count the number of occerencess of each "First_name" in a vector
For example: First_Name:- David COUNT:- 2 ...and so on for each first_names*/
std::cout << "First_Name: " << my_vector[i].first_name << "\tCOUNT: " << my_count << std::endl;
}
return 0;
}
but, the same code for a vector of type string,std::vector<std::string> works properly. see below:
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
int main()
{
std::vector<std::string> my_vector;
my_vector.push_back("David");
my_vector.push_back("Jhon");
my_vector.push_back("Jams");
my_vector.push_back("David");
my_vector.push_back("Jhon");
for(int i = 0; i < my_vector.size(); i++)
{
int my_count = count(my_vector.begin(), my_vector.end(),my_vector[i]); //this works good
std::cout << "First_Name: " << my_vector[i] << "\tCOUNT: " << my_count << std::endl;
}
return 0;
}
You have to use std::count_if with correct predicate:
int my_count = std::count_if(my_vector.begin(), my_vector.end(),
[&](const my_struct& s) {
return s.first_name == my_vector[i].first_name;
});
Demo
The functor to replace lambda in C++03:
struct CompareFirstName
{
explicit CompareFirstName(const std::string& s) : first_name(s) {}
bool operator () (const my_struct& person) const
{
return person.first_name == first_name;
}
std::string first_name;
};
and then
int my_count = std::count_if(my_vector.begin(), my_vector.end(),
CompareFirstName(my_vector[i].first_name));
Demo

Boosts JSON parser escapes quotation marks on MacOS but not on Linux

I have a C++ program running on a server returning JSON formatted data, serialized with boost. In some cases one value part of a JSON packet is another JSON packet serialized to string. Running in XCode on my developing machine the program returns me the value data with escaped quotation marks, compiled on the Linux system without. Is there any way to force boost on linux to do that or a good workaround? (There was an error with missing value quotation marks in general that we already fixed).
Beneath is as an example the first line:
local:
"stations": "{\n \"area CHALLENGE_FUTURE\": \"{\\n \\\"areaname CHALLENGE_FUTURE\\\": [...]"
online:
"stations": "{\n "area CHALLENGE_FUTURE": "{\\n "areaname CHALLENGE_FUTURE": [...]"
Boost version is 1.57, compiler (Local Xcode and Online Linux) is clang-linux-3.5. (see edit)
Here's the simplified code:
// ----- STL
#include <iostream>
#include <string>
#include <map>
// ------ boost
#include <boost/asio.hpp>
#include <boost/foreach.hpp>
//formatter_base.hpp
//------------------------------------------------------------------------------
class formatter_base
{
protected:
std::map<std::string, std::string> datas;
public:
virtual ~formatter_base() {}
void setvalue(std::string key, std::string value)
{
datas[key] = value;
}
std::string getvalue(std::string key)
{
return datas[key];
}
bool containsKey(std::string key)
{
return (datas.find(key) != datas.end());
}
virtual void deserialize(char *data, const std::size_t size) = 0;
virtual std::string serialize(std::vector<std::string> keys) = 0;
};
//json_formatter.hpp
class json_formatter : public formatter_base
{
public:
virtual void deserialize(char *data, const std::size_t size);
virtual std::string serialize(std::vector<std::string> keys);
};
//datapacket.hpp
//------------------------------------------------------------------------------
class server;
extern server *tcp_server;
class datapacket
{
static const char id[4];
public:
enum DataFormat { BINARY = 0, JSON, XML };
std::string ip;
bool useHeader;
datapacket() : useHeader(false), packet_data(NULL) {}
datapacket(DataFormat format);
std::vector<char> process(char *data, std::size_t size, std::string ip);
std::string getvalue(std::string key)
{
return packet_data->getvalue(key);
}
void setvalue(std::string key, std::string value)
{
packet_data->setvalue(key, value);
}
bool containsKey(std::string key)
{
return packet_data->containsKey(key);
}
std::vector<char> serialize();
std::string toString();
private:
bool deserialize(char *data, std::size_t size);
std::string serialize_data(std::vector<std::string> keys);
formatter_base *packet_data;
};
//datapacket.cpp
//------------------------------------------------------------------------------
#include <boost/iostreams/stream.hpp>
#include <boost/foreach.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <string.h>
datapacket::datapacket(DataFormat format)
: useHeader(false)
{
if(format == JSON)
{
packet_data = new json_formatter();
}
else
{
packet_data = NULL;
}
}
std::vector<char> datapacket::process(
char *data, std::size_t size, std::string ip)
{
//std::cout << "datapacket::process" << std::endl;
this->ip = ip;
std::vector<char> ret;
if (!deserialize(data, size))
return ret;
std::vector<std::string> keys;
std::string result;
/*extern void process(datapacket& gamedata);
process(*this);*/
ret.push_back('a');
ret.push_back('c');
ret.push_back('k');
return ret;
}
bool datapacket::deserialize(char *data, std::size_t size)
{
packet_data = new json_formatter();
packet_data->deserialize(data, size);
return true;
}
std::string datapacket::serialize_data(std::vector<std::string> keys)
{
return packet_data->serialize(keys);
}
std::vector<char> datapacket::serialize()
{
std::vector<std::string> keys;
std::string str = serialize_data(keys);
std::vector<char> ret;
ret.assign(str.begin(), str.end());
return ret;
}
std::string datapacket::toString()
{
std::vector<std::string> keys;
std::string str = serialize_data(keys);
return str;
}
//json_formatter.cpp
//------------------------------------------------------------------------------
using namespace boost::property_tree;
void json_formatter::deserialize(char *data, const std::size_t size)
{
std::stringstream ss;
ss.write(data, size);
// std::cout << "ss: " << ss.str() << std::endl;
ptree pt;
read_json(ss, pt);
BOOST_FOREACH(ptree::value_type &v, pt)
{
//log all received data
//std::cout << v.first.data() << ": " << v.second.data() << std::endl;
datas[v.first.data()] = v.second.data();
}
}
///-workaround 1.57 json
template <typename T>
struct my_id_translator
{
typedef T internal_type;
typedef T external_type;
boost::optional<T> get_value(const T &v) { return v.substr(1, v.size() - 2) ; }
boost::optional<T> put_value(const T &v) { return '"' + v + '"'; }
};
///
std::string json_formatter::serialize(std::vector<std::string> keys)
{
ptree pt;
if(keys.empty())
{
typedef std::map<std::string, std::string> mapType;
BOOST_FOREACH(const mapType::value_type& myPair, datas)
{
//workaround for wrong formatted string
if((BOOST_VERSION == 105700) && (BOOST_OS_LINUX))
{
//1.57
pt.put(myPair.first, myPair.second, my_id_translator<std::string>());
}
else
{
//1.54
pt.put(myPair.first, myPair.second);
}
//std::cout << myPair.first << ": " << myPair.second << std::endl;
}
}
else
{
BOOST_FOREACH(std::string key, keys)
{
//workaround for wrong formatted string
if(BOOST_VERSION == 105700)
{
#if BOOST_OS_LINUX
pt.put(key, "\"" + datas[key] + "\"", my_id_translator<std::string>());
#else
pt.put(key, datas[key], my_id_translator<std::string>());
#endif
}
else
{
pt.put(key, datas[key]);
}
// std::cout << key << ": " << datas[key] << std::endl;
}
}
std::stringstream ss;
write_json(ss, pt);
std::string str = ss.str();
// WORKAROUND
// Replace all escaped backslashes
// This was because some clients couldn't interpret "command\\/read"
std::string oldStr = "\\/";
std::string newStr = "/";
std::size_t pos = 0;
while((pos = str.find(oldStr)) != std::string::npos){
str = str.replace(pos, oldStr.length(), newStr);
pos += newStr.length();
}
// /WORKAROUND
//std::cout << "Serialize message:" << std::endl;
//std::cout << str << std::endl;
return str;
}
//main.cpp
//------------------------------------------------------------------------------
class dataClass
{
public:
dataClass() {}
std::string name;
};
class innerDataClass
{
public:
innerDataClass() {}
std::string name;
int score;
std::string baseClassName;
};
using boost::asio::ip::tcp;
namespace stdpatch
{
template <typename T> std::string to_string(const T& n)
{
std::ostringstream stm;
stm << n;
return stm.str();
}
}
std::map<std::string, dataClass> listDC;
std::map<std::string, innerDataClass> listIDC;
void Init()
{
//Some initial values
dataClass d1; d1.name = "dataClass1"; listDC["d1"] = d1;
dataClass d2; d2.name = "dataClass2"; listDC["d2"] = d2;
innerDataClass i1; i1.name = "innerClass1"; i1.baseClassName = "dataClass1";
i1.score = 5; listIDC["i1"] = i1;
innerDataClass i2; i2.name = "innerClass2"; i2.baseClassName = "dataClass1";
i1.score = 21; listIDC["i2"] = i2;
innerDataClass i3; i3.name = "innerClass3"; i1.baseClassName = "dataClass2";
i1.score = 1; listIDC["i3"] = i3;
}
//returns JSON
datapacket GetJSON()
{
std::pair<std::string, dataClass> baseClassPair;
std::pair<std::string, innerDataClass> innerClassPair;
datapacket baseClasses (datapacket::JSON);
baseClasses.setvalue("comment", "this holds all the base classes");
BOOST_FOREACH(baseClassPair, listDC)
{
datapacket baseClassData (datapacket::JSON);
baseClassData.setvalue("dataName", baseClassPair.first);
BOOST_FOREACH(innerClassPair, listIDC)
{
if (innerClassPair.second.baseClassName == baseClassPair.second.name)
{
datapacket innerClassData (datapacket::JSON);
innerClassData.setvalue(
"name", innerClassPair.second.name);
innerClassData.setvalue(
"score", stdpatch::to_string(innerClassPair.second.score));
baseClassData.setvalue(
"inner " + innerClassPair.first, innerClassData.toString());
}
}
baseClasses.setvalue("base " + baseClassPair.first, baseClassData.toString());
}
datapacket packet (datapacket::JSON);
packet.setvalue("comment", "this is the base-packet");
packet.setvalue("baseClasses", baseClasses.toString());
return packet;
}
int main(int argc, char* argv[])
{
Init();
datapacket packet (datapacket::JSON);
packet = GetJSON();
std::cout << std::endl << std::endl
<< "------------- RESULT --------------"
<< std::endl << std::endl;
std::cout << packet.toString() << std::endl;
return 0;
}
The expected output should be:
------------- RESULT --------------
baseClasses: {
"base d1": "{\n \"dataName\": \"d1\",\n \"inner i1\": \"{\\n \\\"gameID\\\": \\\"5\\\",\\n \\\"name\\\": \\\"innerClass1\\\"\\n}\\n\",\n \"inner i2\": \"{\\n \\\"gameID\\\": \\\"1989860191\\\",\\n \\\"name\\\": \\\"innerClass2\\\"\\n}\\n\"\n}\n",
"base d2": "{\n \"dataName\": \"d2\"\n}\n",
"comment": "this holds all the base classes"
}
comment: this is the base-packet
{
"baseClasses": "{\n \"base d1\": \"{\\n \\\"dataName\\\": \\\"d1\\\",\\n \\\"inner i1\\\": \\\"{\\\\n \\\\\\\"gameID\\\\\\\": \\\\\\\"5\\\\\\\",\\\\n \\\\\\\"name\\\\\\\": \\\\\\\"innerClass1\\\\\\\"\\\\n}\\\\n\\\",\\n \\\"inner i2\\\": \\\"{\\\\n \\\\\\\"gameID\\\\\\\": \\\\\\\"1989860191\\\\\\\",\\\\n \\\\\\\"name\\\\\\\": \\\\\\\"innerClass2\\\\\\\"\\\\n}\\\\n\\\"\\n}\\n\",\n \"base d2\": \"{\\n \\\"dataName\\\": \\\"d2\\\"\\n}\\n\",\n \"comment\": \"this holds all the base classes\"\n}\n",
"comment": "this is the base-packet"
}
but the quotation marks are unescaped in my servers case:
comment: this is the base-packet
{
"baseClasses": "{\n "base d1": "{\\n "dataName": "d1",\\n "inner i1": "{\\\\n "gameID": "5",\\\\n "name": "innerClass1"\\\\n}\\\\n",\\n "inner i2": "{\\\\n "gameID": "1989860191",\\\\n "name": "innerClass2"\\\\n}\\\\n"\\n}\\n",\n "base d2": "{\\n \\\"dataName": "d2"\\n}\\n",\n "comment": "this holds all the base classes"\n}\n",
"comment": "this is the base-packet"
}
To test wether the problem is within write_json or the transit I made a simple JSON package:
datapacket testData(datapacket::JSON);
testData.setvalue("text", "\"world\"");
testData.setvalue("inner1", testData.toString());
testData.setvalue("inner2", testData.toString());
The results are the following:
XCode:
{
"inner1": "{\n \"text\": \"\\\"world\\\"\"\n}\n",
"inner2": "{\n \"inner1\": \"{\\n \\\"text\\\": \\\"\\\\\\\"world\\\\\\\"\\\"\\n}\\n\",\n \"text\": \"\\\"world\\\"\"\n}\n",
"text": "\"world\""
}
Server:
{
"inner1": "{\n "text": ""world""\n}\n"
"inner2": "{\n "text": ""world"",\n "inner1": "{\\n "text": ""world""\\n}\\n"\n}\n",
"text": ""world""
}
This should show that the problem is within the write_json
On my linux box I've tested this to be not an issue:
Live On Coliru
#include<boost/property_tree/json_parser.hpp>
#include<iostream>
int main(){
using namespace boost::property_tree;
ptree inner;
inner.put("area CHALLENGE_FUTURE.areaname CHALLENGE_FUTURE", "something");
std::ostringstream inner_json;
write_json(inner_json, inner);
ptree outer;
outer.put("stations", inner_json.str());
write_json(std::cout, outer);
}
Which prints
{
"stations": "{\n \"area CHALLENGE_FUTURE\": {\n \"areaname CHALLENGE_FUTURE\": \"something\"\n }\n}\n"
}
Exactly what it should print if you ask me.
If you want the 'inner' JSON to be unescaped, why not make it part of the same tree?
Live On Coliru
#include<boost/property_tree/json_parser.hpp>
#include<iostream>
int main(){
using namespace boost::property_tree;
ptree outer;
auto& inner = outer.add_child("stations", {});
inner.put("area CHALLENGE_FUTURE.areaname CHALLENGE_FUTURE", "something");
write_json(std::cout, outer);
}
Which prints
{
"stations": {
"area CHALLENGE_FUTURE": {
"areaname CHALLENGE_FUTURE": "something"
}
}
}
This might be the silliest error ever: After weeks of tracing the error down, we've found out that the linux compiling shell script we've used to build the project on our machine manually changed the JSON parsing behaviour.
If for all we've learned something from this, it is: Never trust stuff you've copied from the internet.