Iterate on json structure with Poco - c++

I'm using Poco library for C++ code
Here is an example of the json tree I have to parse
{
"name" : "actionToDo",
"description" : "",
"version" : "1",
"parameters" : {
"inputDirectory" : "string",
"workingDir" : "string",
"tempDir" : "string",
"size" : "integer"
}
}
The amount of data into "parameters" field can change.
I have to put all items into a map
Here my code today
std:: map<std::string, std::string> output;
Poco::JSON::Parser sparser;
Poco::Dynamic::Var result = sparser.parse(jsonStr);
Poco::JSON::Object::Ptr object = result.extract<Poco::JSON::Object::Ptr>();
Poco::DynamicStruct ds = *object;
Poco::Dynamic::Var collection(ds["parameters"]);
if (collection.isStruct())
{
LOG("STRUCT"); //it's logged !!
}
for (Poco::Dynamic::VarIterator it = collection.begin(); it != collection.end(); ++it)
{
LOG_F("item : %s", it->toString()); //never logged
//here I would like to have something like
//output[it->first()] = it->second();
}
And the output I got
14:13:00'900 : : [Notice] : STRUCT
14:13:00'900 : : [Critical] : Exception : Exception: Unable to load Run from file :
/opt/.../file.json Exception:
Unable to parse field 'parameters' or its children
Invalid access: Not a struct.
The "Unable to parse field 'parameters' or its children" is generated by a catch below but the "Invalid access: Not a struct." comes from Poco

use for collection variable DynamicStruct instead of Var
Poco::DynamicStruct collection = ds["parameters"].extract<Poco::DynamicStruct>();
for (auto it = collection.begin(); it != collection.end(); ++it)
{
LOG_F("item : %s", it->second.toString().c_str());
}

Related

libbson help to read data into object C++

i have my unreal project that must read out some BSON document data into a map.
right now i'm able to load that file and print it out with the following code :
void AMyActor::BeginPlay()
{
Super::BeginPlay();
std::ifstream input( filePath , std::ios::binary );
std::vector<unsigned char> buffer(std::istreambuf_iterator<char>(input), {});
bson_t* doc{ bson_new_from_data(buffer.data(),buffer.size()) };
char *str;
if( doc != nullptr )
{
UE_LOG(LogTemp,Warning,TEXT( "Success" ));
str = bson_as_json(doc, NULL);
FString fstr = FString(UTF8_TO_TCHAR(str));
UE_LOG(LogTemp, Warning, TEXT("BSON Output: %s"), *fstr);
}
}
and this is here is where i want to store it :
class Databases
{
std::map<std::string,DatabaseBase> _dictionary;
explicit Databases(const std::map<std::string, DatabaseBase>& dictionary)
: _dictionary(dictionary)
{
}
};
so what i'm looking for is to create a new instance of Databases and initialize "_dictionary" with the content of the bson document.
i'm actually looking for this into the libbson document : http://mongoc.org/libbson/current/bson_t.html
but without success...anyone can help me?
thank's in advance
PS: i'm under unreal but i have linked the libbson library
Update:
since i have to provide how my json file looks like, and DatabaseBase looks like
JSON :
{
"_dictionary" : [ {
"_classID" : "CC00",
"_dictionary" : [ {
"k" : "sample_key",
"v" : ["ACH_00"]
}, {
"k" : "sample_index",
"v" : ["0"]
}]
}, {
"_classID" : "CC01",
"_dictionary" : [ {
"k" : "sample_key",
"v" : ["ACH_01"]
}, {
"k" : "sample_index",
"v" : ["1"]
}]
}]
}
DatabaseBase :
class DatabaseBase
{
public:
DatabaseBase() = default;
std::string sample_key;
int sample_index;
};
Breaking out nlohmann::json:
using nlohmann::json;
std::map<std::string, DatabaseBase> dictionaries;
json input = json::from_bson(buffer);
for (auto& obj : input["_dictionary"]) {
auto name = obj["_classID"];
auto key = obj["_dictionary"][0]["v"][0];
auto idx = stoi(obj["_dictionary"][1]["v"][0].get<std::string>());
auto db = DatabaseBase{key, idx};
dictionaries[name] = db;
}
Databases dbs{dictionaries};
output: (from my debugger)
(lldb) p dbs
(Databases) $0 = {
_dictionary = size=2 {
[0] = {
first = "CC00"
second = (sample_key = "ACH_00", sample_index = 0)
}
[1] = {
first = "CC01"
second = (sample_key = "ACH_01", sample_index = 1)
}
}
}

mongo-cxx-driver decimal values problems

I'm using the mongocxx (v3) driver in my project to store the app settings in the mongodb. While working in windows everything seems to work well. But now that i'm testing in linux i have noticed some problems that i cannot understand why are happening.
i have the following function to retrieve the app settings:
QJsonObject MongoDBManager::getProjectsSettings(QString appid,QString projectsname)
{
try {
if(m_selectedDB.has_collection(projectsname.toStdString())==false){
LOG_ERROR()<<"collection '"+projectsname+"' do not exist";
return QJsonObject();
}
mongocxx::collection selectedCollection = m_selectedDB[projectsname.toStdString()];
auto cursor =selectedCollection.find(document{}
<< "appid"
<< appid.toStdString()
<< finalize
);
// auto cursor = m_selectedCollection.find(document{} << "_id" << appid.toStdString() << finalize);
for (auto &&doc : cursor) {
bsoncxx::builder::stream::document s;
s << "x" << 1.0;
std::cout<<"testing:" << bsoncxx::to_json(s) << std::endl;
std::cout<<"doc:"<< bsoncxx::to_json(doc)<<std::endl;
....
While in run in debug mode i get the following output:
testing:{ "x" : 1.0 }
doc:{ "_id" : { "$oid" : "5e94da26ef664e37b0d8d1e4" }, "appid" : "5ea8a4192483621670ffeb2b", "value" : 1.1000000000000000888 }
and in release i get:
testing:{ "x" : 1.0 }
doc:{ "_id" : { "$oid" : "5e94da26ef664e37b0d8d1e4" }, "appid" : "5ea8a4192483621670ffeb2b", "value" : 1,1000000000000000888 }
I have to strange things happening:
In debug mode i have a valid json document
... "value" : 1.1000000000000000888 }
but in release mode i have comma instead of dots in the decimal separator witch obviously is an invalid JSON document.
... "value" : 1,1000000000000000888 }
In the DB i have 1.1 why i'm getting 1.1000000000000000888 in my program

AppSync query on Global Secondary Index

I'm trying to get a record from a GSI and I'm stucked.
API Schema:
type DriverInfos {
id: String!
status: Int
lastLat: Float
lastLng: Float
idDriver: String # GSI
}
type Query {
getDriverInfosByDriver(idDriver: String): DriverInfos
}
Resolver :
{
"version" : "2017-02-28",
"operation" : "Query",
"index" : "idDriver-index",
"query" : {
## Provide a query expression. **
"expression": "#idDriver = :idDriver",
"expressionNames" : {
"#idDriver" : "idDriver"
},
"expressionValues" : {
":idDriver" : {
"S" : "${ctx.args.idDriver}"
}
}
}
}
Query :
query getDriverInfosByDriver{
getDriverInfosByDriver(idDriver: "1")
{
idDriver
status
lastLat
lastLng
}
}
Return :
{
"data": {
"getDriverInfosByDriver": {
"idDriver": null,
"status": null,
"lastLat": null,
"lastLng": null
}
}
}
GSI is well activated : Name : "idDriver-index" - PartitionKey : idDriver (String)
Try with other ids : 2, 3, ...
It seems that it comes from the resolver. I tried with different resolver but it always return an error.
Thank you in advance for your answers.
The issue is that a Query operation always returns a set of results not just one. If you want to leave your query type like this:
type Query {
getDriverInfosByDriver(idDriver: String): DriverInfos
}
then you should to change your response mapping template to this:
#if($ctx.result.items.size() > 0)
$util.toJson($ctx.result.items[0])
#else
null
#end
If instead the getDriverInfosByDriver query should return multiple info objects then you should change your schema to:
type DriverInfo {
id: String!
status: Int
lastLat: Float
lastLng: Float
idDriver: String # GSI
}
type DriverInfoConnection {
items: [DriverInfo]
nextToken:String
}
type Query {
getDriverInfosByDriver(idDriver: String): DriverInfoConnection
}
You can then leave your response mapping template as the default:
$util.toJson($ctx.result)
and then query it like so
query getDriverInfosByDriver{
getDriverInfosByDriver(idDriver: "1") {
items {
idDriver
status
lastLat
lastLng
}
}
}

Ionic / angulfire2 - Query join reference multiple times

I'm building app with Ionic and angulfire2 and I'm trying to join multiple references from firebase by using the object key.
Database looks following:
{
"achievements" : {
"200" : {
"authorId" : "nGSlhjaDRKh8XdrgxcusU0wdiHN2",
"description" : "I did it"
}
},
"challengeAchievements" : {
"100" : {
"200" : true
}
},
"challenges" : {
"100" : {
"name" : "test challenge"
},
"101" : {
"name" : "test challenge 2"
}
},
"users" : {
"nGSlhjaDRKh8XdrgxcusU0wdiHN2" : {
"email" : "user1#test.com"
},
"wBMX8WOHIpM7dEkzj0hM19OPMbs1" : {
"email" : "user2#test.com"
}
}
}
I would like to join all this data together so that from challenges you get achievements, and from achievements you get the user data.
Currently I'm able to get the achievement details, but not the user data. My provider looks like this at the moment:
getChallengeAchievements(challengeKey) {
return this.rtdb.list(`/challengeAchievements/${challengeKey}`)
.map(achievements => achievements.map((achievement) => {
if (achievement.key)
achievement.details = this.getAchievementDetails(achievement.key);
achievement.user = this.getAchievementUserDetails(achievement.details.authorId);
return achievement;
}));
}
getAchievementDetails(achievementKey?: string): Observable<any> {
if (achievementKey)
return this.rtdb.object(`/achievements/${achievementKey}`);
}
getAchievementUserDetails(authorId?: string): Observable<any> {
if (authorId)
return this.rtdb.object(`/users/${authorId}`);
else console.log('Not found');
}
How should I structure the authorId query in this function? If I use static value in
achievement.details.authorId('nGSlhjaDRKh8XdrgxcusU0wdiHN2')
I'm able to receive the data.
Solved it by subscribing to the first join "achievement.details" and obtaining the user data from there.
getChallengeAchievements(challengeKey) {
return this.rtdb.list(`/challengeAchievements/${challengeKey}`)
.map(achievements => achievements.map((achievement) => {
if (achievement.key)
achievement.details = this.getAchievementDetails(achievement.key);
achievement.details.subscribe(
details => {
achievement.user = this.getAchievementUserDetails(details.authorId);
})
return achievement;
}));
}

Refresh rows in table, which are created from factory function (SAPUI5)

How can I "refresh" the data in rows inside a table? I know, that the table is refreshed, when the model is getting changed. But the problem is, that the rows are created by a factory method. The code for the rows looks like this:
formatter : function(text, id) {
if (text != null && id != null) {
if (this.getProperty("showId")) {
return text + " ( " + id + " )";
} else {
return text;
}
}
return "";
So, when I click on a button "hide ID" the property is getting changed and the table should be refreshed so that the content is built new. How can I do this? I checked the method .refresh() but this didn't work.
EDIT:
This is my column with the factory function:
columns : [ new sap.ui.table.Column({
label : "XYZ( ID )",
filterProperty : "SHORT_TEXT",
template : new sap.m.Label().bindProperty("text", {
parts : [ {
path : "SHORT_TEXT",
type : new sap.ui.model.type.String()
}, {
path : "ID",
type : new sap.ui.model.type.String()
} ],
formatter : function(text, id) {
if (text != null && id != null) {
if (this.getProperty("showId")) {
return text + " ( " + id + " )";
} else {
return text;
}
}
return "";
}.bind(this)
})
})
This is the method, which changes the property:
onShowHideIdRequest : function(oControlEvent) {
if (oControlEvent.getParameter("pressed")) {
this.setProperty("showId", true);
sap.ui.getCore().byId("oShowHideIdButton").setIcon("sap-icon://show");
} else {
this.setProperty("showId", false);
sap.ui.getCore().byId("oShowHideIdButton").setIcon("sap-icon://hide");
}
sap.ui.getCore().byId("oTreeTable").rerender();
},
And the the property looks like this inside my component:
metadata : {
properties : {
showId : {
type : "boolean",
defaultValue : true
}
},
The "oTreeTable" ID refers to a sap.ui.tableTreeTable
I thought for a few days this all works fine, I don't know what's no wrong ...
First of all, the method you have written is formatter not the factory method. There is a difference between formatter and factory method. Formatter is used to return the value of a property of ui5 control based on some conditions or evaluation where as factory method is used to bind aggregations