How to parse recursive json data in duktape? - c++

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.

Related

How to modify node element with boost::property_tree and put_value(const Type &value)

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

C++ thread lambda captured object can read but cannot erase

I'm new in c++ so my problem could be very simple but I cannot solve it.
In my constructor I want to start detached thread which will be looping with class variable and removing old data.
userCache.hpp
struct UserCacheItem {
long m_expires;
oatpp::Object<User> m_user;
UserCacheItem(const long expires, const oatpp::Object<User> &user);
};
class UserCache {
private:
std::map<std::string, std::shared_ptr<UserCacheItem> > m_userCacheItem;
public:
UserCache();
void cacheUser(std::string payload, std::shared_ptr<UserCacheItem> &userCacheItem);
};
userCache.cpp
UserCache::UserCache()
{
std::thread thread([this]() mutable {
while (true){
auto curTimestamp = std::chrono::seconds(std::chrono::seconds(std::time(nullptr))).count();
for(auto &elem : m_userCacheItem){
if (curTimestamp > elem.second->m_expires){
std::cout << "Erasing element: " << elem.second->m_expires << std::endl;
m_userCacheItem.clear();
}
}
std::cout << "Cache size: " << m_userCacheItem.size() << " Current timestamp: " << curTimestamp << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(10));
};
});
thread.detach();
}
When I reach line m_userCacheItem.clear(); I get segmentation fault. Ofcourse if if block is false line with cout cache sizie is printed properly.
So I can read my variable but I cannot modify it :(
Where I'm making error?
You cannot modify the map while you're iterating it
for(auto &elem : m_userCacheItem){
if (curTimestamp > elem.second->m_expires){
std::cout << "Erasing element: " << elem.second->m_expires << std::endl;
m_userCacheItem.clear();
}
}
If you want to erase an element, use std::map::erase:
for(auto & it = m_userCacheItem.begin(); it != m_userCacheItem.end();) {
if(condition) {
it = m_userCacheItem.erase(it);
} else {
++it;
}
}

about : Scope of exception object in C++ : why don't I get the copy?

In question about scope of exception it is stated by Aj. that throw and catch clauses will create copies of the exception (unless reference is used I guess)
I tried myself a small toy code and I don't understand the result. here :
//g++ 7.4.0
#include <iostream>
using namespace std;
struct Some_error {
Some_error(float code):err_code(code){ cout << "Some_error(" << err_code << ")\n"; }
~Some_error() { cout << "~Some_error(" << err_code << ")\n"; }
Some_error(const Some_error& o):err_code(o.err_code+0.1) { cout << "Some_error(copy::" << err_code << ")\n"; }
Some_error(Some_error&& o):err_code(std::move(o.err_code)+.01){ cout << "Some_error(move::" << err_code << ")\n"; }
int get_code() const { return err_code; }
private : float err_code;
};
int do_task() {
if ( false ) return 42; else throw Some_error {1};
cout << "end do_task\n" ;
}
void taskmaster(){
try { auto result = do_task(); cout << "the answer is " << result << "\n" ; }
catch (Some_error e) { cout << "catch Some_error : " << e.get_code() << "\n" ; }
cout << "end taskmaster\n" ;
}
int main() { taskmaster(); }
the trace I get is as follows :
Some_error(1)
Some_error(copy::1.1)
catch Some_error : 1
~Some_error(1.1)
~Some_error(1)
end taskmaster
Now first, as I used no reference here, according to Aj., I would expect 2 copies to happen.
And second, there was a copy, that set err_code to 1.1, but the display is still 1.
Remark: just to be complete, I changed the catch to : catch(Some_error& e),
and then the trace looks fine to me :
Some_error(1)
catch Some_error : 1
~Some_error(1)
end taskmaster
I would expect 2 copies to happen.
Why? Only one copy is made by the catch block. Where would the second copy happen?
set err_code to 1.1, but the display is still 1.
Because get_code returns an int, so the floating point value gets truncated.

accessing elements from nlohmann json

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.

Processing array of generic BSON documents with MongoDB C++ driver

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.