Create a json array in C++ - c++

So im trying to create a json Object in c++ dynamically. I want to add a timestamp and then an array with the data included.
So thats what my json String would look like :
{
"timestep": "2160.00",
"vehicles": [
{
"id": "35092_35092_353",
"x": "6.988270",
"y": "50.872139",
"angle": "-20.812787",
"type": "passenger_P_14_1",
"speed": "0.000000",
"pos": "4.600000",
"lane": "4.600000",
"slope": "4.600000"
},
{
"id": "35092_35092_353",
"x": "6.988270",
"y": "50.872139",
"angle": "-20.812787",
"type": "passenger_P_14_1",
"speed": "0.000000",
"pos": "4.600000",
"lane": "4.600000",
"slope": "4.600000"
},
{
"id": "35092_35092_353",
"x": "6.988270",
"y": "50.872139",
"angle": "-20.812787",
"type": "passenger_P_14_1",
"speed": "0.000000",
"pos": "4.600000",
"lane": "4.600000",
"slope": "4.600000"
}
]
}
Im totally new to C++ and im using the Casablanca ( C++ REST SDK) package.
So im having a really hard time producing the code. And i cant find any working solutions. I found this on the wiki
Create a JSON object:
json::value obj;
obj[L"key1"] = json::value::boolean(false);
obj[L"key2"] = json::value::number(44);
obj[L"key3"] = json::value::number(43.6);
obj[L"key4"] = json::value::string(U("str"));
and that works for me. But how do i create an array?
i tried several things but nothing worked. Maybe theres a better package? But as far as i understood its an official micorosft package for json and http.
Help would be really nice!

There are 2 mechanisms. If you are used to std c++ libraries, this should look familiar. Element vector is derived from std::vector.
json::value::element_vector e;
// the first item in the pair is the array index, the second the value
e.push_back(std::make_pair(json::value(0), json::value(false)));
e.push_back(std::make_pair(json::value(1), json::value::string(U("hello"))));
json::value arr(e);
And, if you prefer a cleaner look, and can accept a less efficient compiled result:
json::value arr;
arr[0] = json::value(false);
arr[1] = json::value(U("hello"));
From your message you have tried a bunch of stuff. If you have tried mechanisms like these but they didn't work, give us a sample program that demontrates the failure and we'll have a crack at it.
To get the basic structure in your file above:
json::value vehicles;
vehicles[0] = // 1st vehicle object
vehicles[1] = // 2nd vehicle object
// etc
json::value root;
root[L"timestep"] = json::number(2160.0);
root[L"vehicles"] = vehicles;

Here's how to create an array dynamically using vector. Assume that you have 10 vehicles to add.
std::vector<web::json::value> arrayVehicles;
for(int i = 0; i < 10; i++)
{
web::json::value vehicle;
std::string vehicleID = "id_prefix_" + std::to_string(i);
vehicle["id"] = web::json::value::string(vehicleID);
vehicle["x"] = web::json::value::number(6.988270);
vehicle["y"] = web::json::value::number(50.872139);
arrayVehicles.push_back(vehicle);
}
web::json::value myJSON;
myJSON["vehicles"] = web::json::value::array(arrayVehicles);

You could put it like this:
json::value vehicle1;
vehicle1[L"id"] = json::value::string(L"35092_35092_353");
vehicle1[L"x"] = json::value::number(6.988270);
vehicle1[L"y"] = json::value::number(50.872139);
json::value vehicle2;
vehicle2[L"id"] = json::value::string(L"35092_35092_353");
vehicle2[L"x"] = json::value::number(1.23456);
vehicle2[L"y"] = json::value::number(6.78901);
json::value vehicles;
vehicles[L"timestamp"] = json::value::number(2160);
vehicles[L"vehicles"] = json::value::array({vehicle1, vehicle2});

Here is another method to produce a json array in Casablanca:
int size = 3;
web::json::value yourJson;
yourJson[U("vehicles")] = web::json::value::array(size);
yourJson[U("vehicles")].as_array()[0] = web::json::value(U("some entry"));
yourJson[U("vehicles")].as_array()[1] = web::json::value(U("another entry"));
//...

If you wish to use the array as an answer on a received http_request (in case below it's a http_request request), you are free to use the following snippet of code as an example:
json::value answer;
auto array = answer.array();
for (size_t i = 0; i < GenFile.GetNumberOfCurves(); i++)
{
web::json::value vehicle;
vehicle[L"smth"] = web::json::value::number(WhatEverArray[i].whatever());
array[i] = vehicle;
}
request.reply(status_codes::OK, array);

The following sample json C++ array of strings works for me.
const char * const line_items_items =
"[\
{\
\"commodity_code\": \"44121903\",\
\"description\": \"Miscellaneous goods\",\
\"upc\": \"65100004327\",\
\"quantity\": \"2\",\
\"unit_of_measurement\": \"M62\",\
\"unit_cost\": \"23.09\",\
\"discount_amount\": \"10.03\",\
\"total_amount\": \"50.03\",\
\"tax_amount\": \"10.05\",\
\"extended_amount\": \"76.04\",\
\"debit_or_credit_indicator\": \"credit\",\
\"net_or_gross_indicator\": \"net\"\
},\
{\
\"commodity_code\": \"44121809\",\
\"description\": \"Miscellaneous goods\",\
\"upc\": \"65100007654\",\
\"quantity\": \"4\",\
\"unit_of_measurement\": \"M66\",\
\"unit_cost\": \"35.09\",\
\"discount_amount\": \"5.06\",\
\"total_amount\": \"0.53\",\
\"tax_amount\": \"8.07\",\
\"extended_amount\": \"96.12\",\
\"debit_or_credit_indicator\": \"debit\",\
\"net_or_gross_indicator\": \"gross\"\
}\
]";

Related

How to remove or erase a "key":"value" pair in a Json::Value object using "key"?

I am working in C++ language, visual studio 2022, and using jsoncpp library for working with Json.
To Give you an Idea, here is an example of Json data I am working with
[
{
"name":"Regina Eagle",
"job":"Biologist",
"salary":"728148120",
"email":"Regina_Eagle6155#y96lx.store",
"city":"Nashville"
},
{
"name":"Julius Baker",
"job":"Fabricator",
"salary":"299380360",
"email":"Julius_Baker9507#voylg.center",
"city":"Las Vegas"
},
{
"name":"Rocco Sawyer",
"job":"Chef Manager",
"salary":"223764496",
"email":"Rocco_Sawyer4620#qu9ml.club",
"city":"San Francisco"
},
{
"name":"Chad Murray",
"job":"Project Manager",
"salary":"43031808",
"email":"Chad_Murray6940#jcf8v.store",
"city":"Bridgeport"
},
{
"name":"Rocco Parker",
"job":"Lecturer",
"salary":"322089172",
"email":"Rocco_Parker202#ag5wi.solutions",
"city":"Indianapolis"
}
]
It's a Json array of objects (with key:value pairs).
I have a set of column heads for eg: {"name","job","salary"}, and I want to sort the json data in a way that each object will have only columns that are in the given set.
This is my approach:
Store json data in a Json::Value object (let us say records).
Iterate through records (as it is an array).
Creating another loop to Iterate through object stored at each index.
Extract the key of the key:value pair and check if it's present in the set or not.
If it's present then continue, else if it isn't then remove that key:value entry from there.
This way we can delete unwanted column while looping through the object.
Here, is the code snippet:
set<string> col {"name","job","salary"};
Json::Value records = [
{
"name":"Regina Eagle",
"job":"Biologist",
"salary":"728148120",
"email":"Regina_Eagle6155#y96lx.store",
"city":"Nashville"
},
{
"name":"Julius Baker",
"job":"Fabricator",
"salary":"299380360",
"email":"Julius_Baker9507#voylg.center",
"city":"Las Vegas"
},
{
"name":"Rocco Sawyer",
"job":"Chef Manager",
"salary":"223764496",
"email":"Rocco_Sawyer4620#qu9ml.club",
"city":"San Francisco"
},
{
"name":"Chad Murray",
"job":"Project Manager",
"salary":"43031808",
"email":"Chad_Murray6940#jcf8v.store",
"city":"Bridgeport"
},
{
"name":"Rocco Parker",
"job":"Lecturer",
"salary":"322089172",
"email":"Rocco_Parker202#ag5wi.solutions",
"city":"Indianapolis"
}
];
for (int i = 0; i<records.size(); i++)
{
for (auto j = records[i].begin(); j != records[i].end(); j++)
{
string key = j.key().asString();
if (col.find(key) != col.end())
{
continue;
}
else
{
records[i].removeMember(key);
}
}
}
It works fine until the 'removeMember' function get to run, and throws an error saying can't increment the value of iterator.
Expression: cannot increment value-initialized map/set iterator
Am I doing something wrong?
Or there is another/better way of doing this ?
Please advice.
Don't remove or add elements in a container you're currently iterating.
The JSON objects are stored in a std::map and removeMember calls std::map::erase. It invalidates the current iterator and it can't be incremented anymore. j++ causes the error.
One approach is to first only store the keys of properties you want to delete, and then to delete the properties in a separate loop.
set<string> col {"name","job","salary"};
Json::Value records = [
{
"name":"Regina Eagle",
"job":"Biologist",
"salary":"728148120",
"email":"Regina_Eagle6155#y96lx.store",
"city":"Nashville"
},
{
"name":"Julius Baker",
"job":"Fabricator",
"salary":"299380360",
"email":"Julius_Baker9507#voylg.center",
"city":"Las Vegas"
},
{
"name":"Rocco Sawyer",
"job":"Chef Manager",
"salary":"223764496",
"email":"Rocco_Sawyer4620#qu9ml.club",
"city":"San Francisco"
},
{
"name":"Chad Murray",
"job":"Project Manager",
"salary":"43031808",
"email":"Chad_Murray6940#jcf8v.store",
"city":"Bridgeport"
},
{
"name":"Rocco Parker",
"job":"Lecturer",
"salary":"322089172",
"email":"Rocco_Parker202#ag5wi.solutions",
"city":"Indianapolis"
}
];
for (int i = 0; i<records.size(); i++)
{
std::vector<std::string> toRemove;
for (auto j = records[i].begin(); j != records[i].end(); j++)
{
string key = j.key().asString();
if (col.find(key) != col.end())
{
continue;
}
else
{
// records[i].removeMember(key);
toRemove.push_back(key);
}
}
for (const auto &key : toRemove)
{
records[i].removeMember(key);
}
}

nlohmann json insert value array partially to already existing data

I have a particular case which I am trying to solve with minimal changes if possible.
one of the data is
js["key1"]["subkey2"]["subsubkey3"].push_back({1,2,3,{4,5}});
[ 1,2,3,[[4,5]] ]
Later at some stage I want to insert
{1,2,3,{4,6}}
Then it should become
[ 1,2,3,[[4,5],[4,6]] ]
How can I make this possible without making 1,2,3 value as key?
I did some playing. I didn't get the results you were looking for. Here's my code and results so far.
#include <iostream>
#include <json.hpp>
using namespace std;
using JSON = nlohmann::json;
int main() {
JSON json = JSON::object();
JSON key1JSON = JSON::object();
JSON key2JSON = JSON::object();
JSON key3JSON = JSON::array();
key3JSON.push_back( {1,2,3, {4,5} } );
key3JSON.push_back( {6} );
key2JSON["subsubkey3"] = key3JSON;
key1JSON["subkey2"] = key2JSON;
json["key1"] = key1JSON;
cout << json.dump(2) << endl;
}
Output:
{
"key1": {
"subkey2": {
"subsubkey3": [
[
1,
2,
3,
[
4,
5
]
],
[
6
]
]
}
}
}
You'll see that the first push_back pushed an array inside an array, which is probably one level deeper than you wanted, and the second one added a second array, which is also not what you want.
Which means you're probably going to have to write your own method, especially as you want to also handle uniqueness. I personally never free-format data that way you have in your example. But maybe your method would look something like:
bool contains(const JSON &json, const JSON &value) {
... this looks like fun to write.
}
void appendUnique(JSON &json, const JSON &array) {
for (JSON & thisJson: array) {
if (!contains(json, thisJson)) {
json.push_back(thisJson);
}
}
}
I modified my code like this:
void appendUnique(JSON &json, const JSON & array) {
for (const JSON & thisJSON: array) {
json.push_back(thisJSON);
}
}
...
appendUnique(key3JSON, {1,2,3, {4,5} } );
appendUnique(key3JSON, {6} );
And got this:
{
"key1": {
"subkey2": {
"subsubkey3": [
1,
2,
3,
[
4,
5
],
6
]
}
}
}
I'm not going to write the isUnique method. But I think you may have to take this to conclusion.

How to get JSON objects value using casablanca in C++

I am new to Json. And i m using codeplex (casablanca) for reading the json values.
Below is the sample json string:
[{ "rollno": 2000,
"name": "suhani","marks":{"grade":"C"} }, {"rollno": 3000,"name": "ishu", "marks":{ "grade":"A"} }]
The code to access name & rollno, i am writing below code:
json::value jobj = json::value::parse(utility::conversions::to_string_t(resultbody));
for (unsigned int i = 0; i < jobj.size(); i++) {
auto getval = jobj[i];
if (getval.at(U("name")).is_string()) {
auto xstr = getval.at(U("name")).as_string();
std::string wide = utility::conversions::to_utf8string(xstr);
std::string str(wide.begin(), wide.end());
string name = str;
}
if (getval.at(U("rollno")).is_integer()) {
auto xstr = getval.at(U("rollno")).as_integer();
int rollno = xstr;
} }
HOW TO GET VALUE AT GRADE ?? When i access marks it is of type object, i am not getting how to access grade from there. Please respond.
Marks is still a json object. You'll need to access the grade property. From your code snippet add the following:
for (unsigned int i = 0; i < jobj.size(); i++) {
auto getval = jobj[i];
auto marks_object = getval.at(U("marks"));
auto grade_value = marks_object.at(U("grade")).as_string();

web service calling problem?

When I call a web service data does not come in the proper way. Some blocks are there string, integer, chars are there means mixed data in json form.
1) When I use this approch to convert the data...
StringBuffer sb = new StringBuffer();
byte[] buf = new byte[256];
int n = 0;
while ((n = StrReader.read(buf)) > 0)
{
sb.append(new String(buf,0,n));
}
String returnContent = sb.toString();
System.out.println(new String(returnContent));
StrReader.close();
}
output...
text/htmlj
~"115.252.128.200", "roles": { "1": "anonymous user" }, "session": "", "cache": 0 } } }
No stack trace
2) and when I use this approch to convert the data...
dis = new DataInputStream(hc.openInputStream());
byte[] data1 = new byte[20];
int len = 0;
StringBuffer strBuffer = new StringBuffer();
while ( -1 != (len = dis.read(data1)) )
{
received = new String(data1, 0, len);
System.out.println(received);
}
OUTPUT....
text/html
j
~Salse, "#data": { "se
ssid": "fef51cf48aca
46e3b3aedafc02860f25
", "user": { "uid":
0, "hostname": "115.
252.128.200", "roles
": { "1": "anonymous
user" }, "session":
"", "cache": 0 } }
}
Outer---->>>}
No stack trace
NOTE.... the 'received' variable loses our data when it come out of loop...
The question appears to be: why does the local variable 'received' have only a fragment of the text that is printed to the console. It is because the variable is assigned a new string for each batch of bytes that are read from the DataInputStream.
This code does not appear to concisely get the job done - why use a DataInputStream for example - so perhaps asking about the larger task at hand will be more useful.

Creating JSON arrays in Boost using Property Trees

I'm trying to create a JSON array using boost property trees.
The documentation says: "JSON arrays are mapped to nodes. Each element is a child node with an empty name."
So I'd like to create a property tree with empty names, then call write_json(...) to get the array out. However, the documentation doesn't tell me how to create unnamed child nodes. I tried ptree.add_child("", value), but this yields:
Assertion `!p.empty() && "Empty path not allowed for put_child."' failed
The documentation doesn't seem to address this point, at least not in any way I can figure out. Can anyone help?
Simple Array:
#include <boost/property_tree/ptree.hpp>
using boost::property_tree::ptree;
ptree pt;
ptree children;
ptree child1, child2, child3;
child1.put("", 1);
child2.put("", 2);
child3.put("", 3);
children.push_back(std::make_pair("", child1));
children.push_back(std::make_pair("", child2));
children.push_back(std::make_pair("", child3));
pt.add_child("MyArray", children);
write_json("test1.json", pt);
results in:
{
"MyArray":
[
"1",
"2",
"3"
]
}
Array over Objects:
ptree pt;
ptree children;
ptree child1, child2, child3;
child1.put("childkeyA", 1);
child1.put("childkeyB", 2);
child2.put("childkeyA", 3);
child2.put("childkeyB", 4);
child3.put("childkeyA", 5);
child3.put("childkeyB", 6);
children.push_back(std::make_pair("", child1));
children.push_back(std::make_pair("", child2));
children.push_back(std::make_pair("", child3));
pt.put("testkey", "testvalue");
pt.add_child("MyArray", children);
write_json("test2.json", pt);
results in:
{
"testkey": "testvalue",
"MyArray":
[
{
"childkeyA": "1",
"childkeyB": "2"
},
{
"childkeyA": "3",
"childkeyB": "4"
},
{
"childkeyA": "5",
"childkeyB": "6"
}
]
}
What you need to do is this piece of fun. This is from memory, but something like this works for me.
boost::property_tree::ptree root;
boost::property_tree::ptree child1;
boost::property_tree::ptree child2;
// .. fill in children here with what you want
// ...
ptree.push_back( std::make_pair("", child1 ) );
ptree.push_back( std::make_pair("", child2 ) );
But watch out there's several bugs in the json parsing and writing. Several of which I've submitted bug reports for - with no response :(
EDIT: to address concern about it serializing incorrectly as {"":"","":""}
This only happens when the array is the root element. The boost ptree writer treats all root elements as objects - never arrays or values. This is caused by the following line in boost/propert_tree/detail/json_parser_writer.hpp
else if (indent > 0 && pt.count(Str()) == pt.size())
Getting rid of the "indent > 0 &&" will allow it to write arrays correctly.
If you don't like how much space is produced you can use the patch I've provided here
When starting to use Property Tree to represent a JSON structure I encountered similar problems which I did not resolve. Also note that from the documentation, the property tree does not fully support type information:
JSON values are mapped to nodes containing the value. However, all type information is lost; numbers, as well as the literals "null", "true" and "false" are simply mapped to their string form.
After learning this, I switched to the more complete JSON implementation JSON Spirit. This library uses Boost Spirit for the JSON grammar implementation and fully supports JSON including arrays.
I suggest you use an alternative C++ JSON implementation.
In my case I wanted to add an array to a more or less arbitrary location, so, like Michael's answer, create a child tree and populate it with array elements:
using boost::property_tree::ptree;
ptree targetTree;
ptree arrayChild;
ptree arrayElement;
//add array elements as desired, loop, whatever, for example
for(int i = 0; i < 3; i++)
{
arrayElement.put_value(i);
arrayChild.push_back(std::make_pair("",arrayElement))
}
When the child has been populated, use the put_child() or add_child() function to add the entire child tree to the target tree, like this...
targetTree.put_child(ptree::path_type("target.path.to.array"),arrayChild)
the put_child function takes a path and a tree for an argument and will "graft" arrayChild into targetTree
As of boost 1.60.0, problem persists.
Offering a Python 3 workaround (Gist), which can be syscalled just after boost::property_tree::write_json.
#!/usr/bin/env python3
def lex_leaf(lf: str):
if lf.isdecimal():
return int(lf)
elif lf in ['True', 'true']:
return True
elif lf in ['False', 'false']:
return False
else:
try:
return float(lf)
except ValueError:
return lf
def lex_tree(j):
tj = type(j)
if tj == dict:
for k, v in j.items():
j[k] = lex_tree(v)
elif tj == list:
j = [lex_tree(l) for l in j]
elif tj == str:
j = lex_leaf(j)
else:
j = lex_leaf(j)
return j
def lex_file(fn: str):
import json
with open(fn, "r") as fp:
ji = json.load(fp)
jo = lex_tree(ji)
with open(fn, 'w') as fp:
json.dump(jo, fp)
if __name__ == '__main__':
import sys
lex_file(sys.argv[1])
If you want JSON in C++, there's no need for Boost. With this library you can get JSON as a first class data type that behaves like an STL container.
// Create JSON on the fly.
json j2 = {
{"pi", 3.141},
{"happy", true},
{"name", "Niels"},
{"nothing", nullptr},
{"answer", {
{"everything", 42}
}},
{"list", {1, 0, 2}},
{"object", {
{"currency", "USD"},
{"value", 42.99}
}}
};
// Or treat is as an STL container; create an array using push_back
json j;
j.push_back("foo");
j.push_back(1);
j.push_back(true);
// also use emplace_back
j.emplace_back(1.78);
// iterate the array
for (json::iterator it = j.begin(); it != j.end(); ++it) {
std::cout << *it << '\n';
}
Confused with the official document and the above answers.
Below is what I understand.
Property Tree consists of nodes.
Each node is like below
struct ptree
{
map<key_name,value> data;
vector<pair<key_name,ptree>> children;
};
To put 'value' into data with 'put'
To put 'node' into children with 'push_back'\
// Write
bt::ptree root;
bt::ptree active;
bt::ptree requested;
bt::ptree n1, n2, n3;
n1.put("name", "Mark");
n1.put("age", 20);
n1.put("job", "aaa");
n2.put("name", "Rosie");
n2.put("age", "19");
n2.put("job", "bbb");
n3.put("name", "sunwoo");
n3.put("age", "10");
n3.put("job", "ccc");
active.push_back ({ "",l1 });
active.push_back ({ "",l2 });
requested.push_back({ "",l3 });
root.push_back ({"active", active});
root.push_back ({"requested", requested});
bt::write_json("E:\\1.json", root);
// READ
bt::ptree root2;
bt::ptree active2;
bt::ptree requested2;
bt::ptree r1, r2, r3;
bt::read_json("E:\\1.json", root2);
// loop children
for (auto& [k,n] : root.get_child("active"))
{
cout << n.get<string>("name", "unknown");
cout << n.get<int> ("age" , 11);
cout << n.get<string>("job" , "man");
cout << endl << flush;
}