My JSON file resembles this
{
"active" : false,
"list1" : ["A", "B", "C"],
"objList" : [
{
"key1" : "value1",
"key2" : [ 0, 1 ]
}
]
}
Using nlohmann json now, I've managed to store it and when I do a dump jsonRootNode.dump(), the contents are represented properly.
However I can't find a way to access the contents.
I've tried jsonRootNode["active"], jsonRootNode.get() and using the json::iterator but still can't figure out how to retrieve my contents.
I'm trying to retrieve "active", the array from "list1" and object array from "objList"
The following link explains the ways to access elements in the JSON. In case the link goes out of scope here is the code
#include <json.hpp>
using namespace nlohmann;
int main()
{
// create JSON object
json object =
{
{"the good", "il buono"},
{"the bad", "il cativo"},
{"the ugly", "il brutto"}
};
// output element with key "the ugly"
std::cout << object.at("the ugly") << '\n';
// change element with key "the bad"
object.at("the bad") = "il cattivo";
// output changed array
std::cout << object << '\n';
// try to write at a nonexisting key
try
{
object.at("the fast") = "il rapido";
}
catch (std::out_of_range& e)
{
std::cout << "out of range: " << e.what() << '\n';
}
}
In case anybody else is still looking for the answer.. You can simply access the contents using the same method as for writing to an nlohmann::json object. For example to get values from
json in the question:
{
"active" : false,
"list1" : ["A", "B", "C"],
"objList" : [
{
"key1" : "value1",
"key2" : [ 0, 1 ]
}
]
}
just do:
nlohmann::json jsonData = nlohmann::json::parse(your_json);
std::cout << jsonData["active"] << std::endl; // returns boolean
std::cout << jsonData["list1"] << std::endl; // returns array
If the "objList" was just an object, you can retrieve its values just by:
std::cout << jsonData["objList"]["key1"] << std::endl; // returns string
std::cout << jsonData["objList"]["key2"] << std::endl; // returns array
But since "objList" is a list of key/value pairs, to access its values use:
for(auto &array : jsonData["objList"]) {
std::cout << array["key1"] << std::endl; // returns string
std::cout << array["key2"] << std::endl; // returns array
}
The loop runs only once considering "objList" is array of size 1.
Hope it helps someone
I really like to use this in C++:
for (auto& el : object["list1"].items())
{
std::cout << el.value() << '\n';
}
It will loop over the the array.
Related
I am fairly new to boost::property_tree and I am having a little trouble with what should be a simple task.
I have a default xml file of which is to be copied and made unique with parameters passed into via the ptree & modelModifier(...) function below. All I want to do is parse the xml into a ptree, and then modify the name field (amongst others, but lets just start with name) from "default" to whatever name is passed in from the object_name variable, then write that back into the original ptree.
The function pTreeIterator just iterates through each child and displays its contents.
xml
<?xml version='1.0'?>
<sdf version='1.7'>
<model name='default'>
<link name='link'>
<inertial>
<mass>3.14999</mass>
<inertia>
<ixx>2.86712</ixx>
<ixy>0</ixy>
<ixz>0</ixz>
<iyy>2.86712</iyy>
<iyz>0</iyz>
<izz>0.524998</izz>
</inertia>
<pose>0 0 0 0 -0 0</pose>
</inertial>
</link>
</model>
</sdf>
Code
void ptreeIterator(ptree & pt)
{
using boost::property_tree::ptree;
for (auto&it : pt)
{
std::cout << __FUNCTION__ << std::endl;
std::cout << "------------------------------------------------------" << std::endl;
std::cout << "Iteration output: " << std::endl;
std::cout << "1: pt1: " << it.first << std::endl;
if(pt.get_child_optional(it.first))
{
cout << "pt.get_child_optional(it.first) ---> " << it.first << endl;
ptree pt2 = pt.get_child(it.first);
for (auto&it2 : pt2)
{
std::cout << "\t2: " << "pt2: " << it2.first << " :: " << (std::string)it2.second.data() << std::endl;
if(pt2.get_child_optional(it2.first))
{
ptree pt3 = pt2.get_child(it2.first);
for (auto&it3 : pt3)
{
std::cout << "\t\t3: " << "pt3: " << it3.first << " :: " << (std::string)it3.second.data() << std::endl;
}
}
}
}
}
}
ptree & modelModifier(ptree &model, double px, double py, std::string object_name, uint16_t height)
{
for(auto &it:model){
cout << "it.first = " << it.first << endl;
if(it.first=="model"){
cout << "MODEL TAG" << endl;
model.put_value(object_name);
}
ptreeIterator(model);
}
}
int main(){
ptree ptModel;
const string filenameToInsert = "model.sdf";
std::ifstream ifsModel(filenameToInsert,std::ifstream::binary);
read_xml(ifsModel, ptModel, boost::property_tree::xml_parser::trim_whitespace);
modelModifier(ptModel, 0, 0, "TEST1234567", 30);
return 0;
}
Output
it.first = model
it.second.data()
ptreeIterator
------------------------------------------------------
Iteration output:
1: pt1: model
pt.get_child_optional(it.first) ---> model
2: pt2: <xmlattr> ::
3: pt3: name :: default
Expected Output
it.first = model
it.second.data()
ptreeIterator
------------------------------------------------------
Iteration output:
1: pt1: model
pt.get_child_optional(it.first) ---> model
2: pt2: <xmlattr> ::
3: pt3: name :: TEST1234567
Firstly, your code has UB, since modelModifier doesn't return a value.
The C-style cast in (std::string)it2.second.data() is extremely dangerous as it risks reinterpret_cast-ing unrelated types. There is no reason whatsoever for this kind of blunt casting. Just remove the cast!
Also, ptreeIterator should probably take a ptree const&, not ptree&.
With these fixed, the sample does NOT show the output you claim, instead it prints (Live On Coliru)
it.first = sdf
ptreeIterator
------------------------------------------------------
Iteration output:
1: pt1: sdf
pt.get_child_optional(it.first) ---> sdf
2: pt2: <xmlattr> ::
3: pt3: version :: 1.7
2: pt2: model ::
3: pt3: <xmlattr> ::
3: pt3: link ::
Now even in your question output, you clearly see the difference between the model node and its name attribute, which apparently you want to modify. Just write the code to access that:
it.second.get_child("<xmlattr>.name").put_value(object_name);
This would be correct, assuming that the attribute always exists and instead of ptModel you pass ptModel.get_child("sdf") to modifyModel).
Other Notes: SIMPLIFY!
That said, please simplify the whole thing!
ptree pt2 = pt.get_child(it.first);
Should have been something like
ptree const& pt2 = it.second;
And
the use of get_child_optional only to repeat with get_child is even more wasteful
Good practice is to separate output/query and mutation. So don't call ptreeIterator from inside modelModifier
Also, give functions a good descriptive name (so that you don't have sheepishly explain "The function pTreeIterator just iterates through each child and displays its contents" - just call it displayModel?)
Instead of painstakingly (and flawed) iterating the particular model and printing it in pretty confusing bespoke manner, just use write_xml/write_info/write_json to dump it in a reliable manner.
Listing
Live On Coliru
namespace Model {
void display(ptree const& pt)
{
write_json(std::cout << __FUNCTION__ << "\n---------------\n", pt);
}
void modify(ptree& model, double, double, std::string object_name, uint16_t)
{
for (auto& it : model) {
std::cout << "root = " << it.first << std::endl;
it.second.get_child("model.<xmlattr>.name").put_value(object_name);
}
}
}
Prints
root = sdf
display
---------------
{
"sdf": {
"<xmlattr>": {
"version": "1.7"
},
"model": {
"<xmlattr>": {
"name": "TEST1234567"
},
"link": {
"<xmlattr>": {
"name": "link"
},
"inertial": {
"mass": "3.14999",
"inertia": {
"ixx": "2.86712",
"ixy": "0",
"ixz": "0",
"iyy": "2.86712",
"iyz": "0",
"izz": "0.524998"
},
"pose": "0 0 0 0 -0 0"
}
}
}
}
}
Bonus
In the case that the name attribute might not already exist, the following code would create it on the fly:
void modify(ptree& model, double, double, std::string object_name, uint16_t)
{
ptree value;
value.put_value(object_name);
for (auto& it : model) {
std::cout << "root = " << it.first << std::endl;
it.second.put_child("model.<xmlattr>.name", value);
}
}
I have a very quick question...
I'm using nlohmann's json library.
My issue is that when I go to print a element, the program stops responding.
My json file
{
"Consoleprinting": false,
"Input" : [{"Code" : [{"Name": "EC", "Keybind": "VK_NUMPAD1"}] }]
}
What I have tried.
nlohmann::json jsonData = nlohmann::json::parse(i_Read);
std::cout << jsonData << std::endl;
for (auto& array : jsonData["Input"]) {
std::cout << array["Code"] << std::endl;
}
^ This works but it prints out
[{"Name": "EC", "Keybind": "VK_NUMPAD1"}]
How can I get this it print out just the name?
array["Code"] is an array containing a single collection of key-value pairs, so you need to write:
std::cout << array["Code"][0]["Name"] << std::endl;
I am interested in seeing how we can use Qt's QJsonDocument to parse all entries from a simple nested JSON (as I have just started studying this).
nested json example:
{
"city": "London",
"time": "16:42",
"unit_data":
[
{
"unit_data_id": "ABC123",
"unit_data_number": "21"
},
{
"unit_data_id": "DEF456",
"unit_data_number": "12"
}
]
}
I can parse the non-nested parts of it like so:
QJsonObject jObj;
QString city = jObj["city"].toString();
QString time = jObj["time"].toString();
I am not sure what you are asking, but perhaps this might help:
QJsonDocument doc;
doc = QJsonDocument::fromJson("{ "
" \"city\": \"London\", "
" \"time\": \"16:42\", "
" \"unit_data\": "
" [ "
" { "
" \"unit_data_id\": \"ABC123\", "
" \"unit_data_number\": \"21\" "
" }, "
" { "
" \"unit_data_id\": \"DEF456\", "
" \"unit_data_number\": \"12\" "
" } "
" ] "
" }");
// This part you have covered
QJsonObject jObj = doc.object();
qDebug() << "city" << jObj["city"].toString();
qDebug() << "time" << jObj["time"].toString();
// Since unit_data is an array, you need to get it as such
QJsonArray array = jObj["unit_data"].toArray();
// Then you can manually access the elements in the array
QJsonObject ar1 = array.at(0).toObject();
qDebug() << "" << ar1["unit_data_id"].toString();
// Or you can loop over the items in the array
int idx = 0;
for(const QJsonValue& val: array) {
QJsonObject loopObj = val.toObject();
qDebug() << "[" << idx << "] unit_data_id : " << loopObj["unit_data_id"].toString();
qDebug() << "[" << idx << "] unit_data_number: " << loopObj["unit_data_number"].toString();
++idx;
}
The output I get is:
city "London"
time "16:42"
"ABC123"
[ 0 ] unit_data_id : "ABC123"
[ 0 ] unit_data_number: "21"
[ 1 ] unit_data_id : "DEF456"
[ 1 ] unit_data_number: "12"
In JSON notation, everything should be formatted in key-value. Keys are always strings, but values could be string literals ("example"), number literals , arrays ([]) and objects ({}).
QJsonDocument::fromJson(...).object() returns the root object of a given JSON string. Recall that objects are written by {} notation. This method gives you a QJsonObject. This JSON object has 3 keys ("city", "name" and "unit_data") which value of these keys are of type string literal, string literal and array respectively.
So if you want to access the data stored in that array you should do:
QJsonArray array = rootObj["unit_data"].toArray();
Note that arrays don't have keys, they have only values which could be of the three types mentioned above. In this case, the array holds 2 objects which can be treated as other JSON objects. So,
QJsonObject obj = array.at(0).toObject();
Now the obj object points to the following object:
{
"unit_data_id": "ABC123",
"unit_data_number": "21"
}
So, you should now be able to do what you want. :)
It can happen that one of the elements inside your JSON has more elements inside. It can also happen that you don't know the characteristics of the file (or you want to have a general function).
Therefore you can use a function for any JSON:
void traversJson(QJsonObject json_obj){
foreach(const QString& key, json_obj.keys()) {
QJsonValue value = json_obj.value(key);
if(!value.isObject() ){
qDebug() << "Key = " << key << ", Value = " << value;
}
else{
qDebug() << "Nested Key = " << key;
traversJson(value.toObject());
}
}
};
I have the following document in my MongoDB test database:
> db.a.find().pretty()
{
"_id" : ObjectId("5113d680732fb764c4464fdf"),
"x" : [
{
"a" : 1,
"b" : 2
},
{
"a" : 3,
"b" : 4
}
]
}
I'm trying to access and process the elements in the "x" array. However, it seems that the Mongo driver is identifying it not as an array of JSON document, but as Date type, as shown in the following code:
auto_ptr<DBClientCursor> cursor = c.query("test.a", BSONObj());
while (cursor->more()) {
BSONObj r = cursor->next();
cout << r.toString() << std::endl;
}
which output is:
{ _id: ObjectId('51138456732fb764c4464fde'), x: new Date(1360233558334) }
I'm trying to follow the documentation in http://api.mongodb.org/cplusplus and http://docs.mongodb.org/ecosystem/drivers/cpp-bson-array-examples/, but it is quite poor. I have found other examples of processing arrays, but always with simple types (e.g. array of integer), but not when the elements in the array are BSON documents themselves.
Do you have some code example of procesing arrays which elements are generic BSON elements, please?
you could use the .Array() method or the getFieldDotted() method: as in the following:
Query query = Query();
auto_ptr<DBClientCursor> cursor = myConn.query("test.a", query);
while( cursor->more() ) {
BSONObj nextObject = cursor->next();
cout << nextObject["x"].Array()[0]["a"] << endl;
cout << nextObject.getFieldDotted("x.0.a") << endl;
}
At the end, it seems that embeddedObject() method was the key:
auto_ptr<DBClientCursor> cursor = c.query("test.a", BSONObj());
while (cursor->more()) {
BSONObj r = cursor->next();
cout << "Processing JSON document: " << r.toString() << std::endl;
std::vector<BSONElement> be = r.getField("x").Array();
for (unsigned int i = 0; i<be.size(); i++) {
cout << "Processing array element: " << be[i].toString() << std::endl;
cout << " of type: " << be[i].type() << std::endl;
BSONObj bo = be[i].embeddedObject();
cout << "Processing a field: " << bo.getField("a").toString() << std::endl;
cout << "Processing b field: " << bo.getField("b").toString() << std::endl;
}
}
I was wrongly retrieving a different ObjectID and a different type (Date instead of array) becuase I was looking to a different collection :$
Sorry for the noise. I hope that the fragment above helps others to figure out how to manipulate arrays using the MongoDB C++ driver.
I am trying to parse an recursive data structure with duktape and seem to have an error somewhere.
Since i am new to the duktape library and the examples are not clear on this situation, i thought i could ask the crowd.
The data:
{
"type": "window",
"children": [
{
"type": "button"
},
{
"type": "button"
}
]
}
With the functions used to parse it:
void parse_config() {
duk_context* context = duk_create_heap_default();
std::cout << "duk_create_heap_default" << std::endl;
duk_push_string(context, input.c_str());
std::cout << "duk_push_string" << std::endl;
duk_json_decode(context, 0);
std::cout << "duk_json_decode" << std::endl;
parse_widget(context);
}
void parse_widget(duk_context* context) {
duk_get_prop_string(context, 0, "type");
std::cout << "duk_get_prop_string" << std::endl;
auto type = duk_get_string(context, 1);
std::cout << "duk_get_string" << std::endl;
duk_pop(context);
std::cout << "duk_pop" << std::endl;
std::cout << "type: " << type << std::endl;
if (duk_has_prop_string(context, 0, "children")) {
std::cout << "duk_has_prop_string" << std::endl;
parse_children(context);
} else {
std::cout << "duk_has_prop_string" << std::endl;
std::cout << "no children" << std::endl;
}
}
void parse_children(duk_context* context) {
duk_get_prop_string(context, 0, "children");
std::cout << "duk_get_prop_string" << std::endl;
if (duk_is_array(context, 1)) {
std::cout << "duk_is_array" << std::endl;
duk_enum(context, 1, 0);
std::cout << "duk_enum" << std::endl;
while (duk_next(context, 2, 0)) {
std::cout << "duk_next" << std::endl;
std::cout << "parse child" << std::endl;
parse_widget(context);
}
}
}
With this version i get the output:
$ ./programm
duk_create_heap_default
duk_push_string
duk_json_decode
duk_get_prop_string
duk_get_string
duk_pop
type: window
duk_has_prop_string
duk_get_prop_string
duk_is_array
duk_enum
duk_next
parse child
duk_get_prop_string
duk_get_string
duk_pop
type: $
The correct output should of course be:
duk_create_heap_default
duk_push_string
duk_json_decode
duk_get_prop_string
duk_get_string
duk_pop
type: window
duk_has_prop_string
duk_get_prop_string
duk_is_array
duk_enum
duk_next
parse child
duk_get_prop_string
duk_get_string
duk_pop
type: button
duk_has_prop_string
no children
duk_next
parse child
duk_get_prop_string
duk_get_string
duk_pop
type: button
duk_has_prop_string
no children
So the question remains: where is my error in parsing the data?
There are two problems I can see:
You're using value stack indices 0 and 1 in parse_widget/parse_children which will be incorrect once you recurse. Note that the stack index 0 does not change when you do normal C function calls (but does change when you call into a native/Ecmascript function using e.g. duk_call()). You should use negative indices to refer to items relative to stack top; you could also use non-negative indices but keep track of a base index to the current widget being processed.
There's a duk_pop() missing for the children array, as well as for the enumerated key and value in the duk_next() loop.