libbson help to read data into object C++ - 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)
}
}
}

Related

EspHome custom component

i have a probleme with custom code in esphome..
There is the error :
src/screen.h:23:59: error: cannot convert 'MyCustomComponent::MyCustomComponent(esphome::template_::TemplateNumber*&, esphome::template_::TemplateNumber*&, esphome::template_::TemplateNumber*&, esphome::homeassistant::HomeassistantTextSensor*&)::<lambda(String)>' to 'std::function<void(std::__cxx11::basic_string<char>)>'
23 | { str = str_n; });
| ^
and the code :
MyCustomComponent(esphome::template_::TemplateNumber *&_led_r,esphome::template_::TemplateNumber *&_led_g,esphome::template_::TemplateNumber *&_led_b,esphome::homeassistant::HomeassistantTextSensor *&_str)
{
_led_r->add_on_state_callback([this](float led_r_n)
{ led_r = led_r_n; });
_led_g->add_on_state_callback([this](float led_g_n)
{ led_g = led_g_n; });
_led_b->add_on_state_callback([this](float led_b_n)
{ led_b = led_b_n; });
_str->add_on_state_callback([this](String str_n)
{ str = str_n; });
}
I'm really lost..
thank you in advance
EDIT :
here is my code :
here is my code, in case I specify that I run this on a HomeAssistant instance with EspHome in version 2022.8.3
#include "esphome.h"
#include <Wire.h>
#include "rgb_lcd.h"
class MyCustomComponent : public Component, public CustomAPIDevice {
public:
rgb_lcd lcd;
bool enable = false;
int led_r = 0;
int led_g = 0;
int led_b = 0;
String str = "ff";
MyCustomComponent(esphome::template_::TemplateNumber *&_led_r,esphome::template_::TemplateNumber *&_led_g,esphome::template_::TemplateNumber *&_led_b,esphome::homeassistant::HomeassistantTextSensor *&_str)
{
_led_r->add_on_state_callback([this](float led_r_n)
{ led_r = led_r_n; });
_led_g->add_on_state_callback([this](float led_g_n)
{ led_g = led_g_n; });
_led_b->add_on_state_callback([this](float led_b_n)
{ led_b = led_b_n; });
_str->add_on_state_callback([this](String str_n)
{ str = str_n; });
}
void setup() override {
lcd.begin(16, 2);
}
void loop() override
{
lcd.setRGB(led_r,led_g,led_b);
}
As shown here
void esphome::text_sensor::TextSensor::add_on_state_callback(std::function< void(std::string)>callback)
You should use std::string instead of String (Arduino string), those are incompatible.
I'm not sure what you're going to do with String str = "ff"; but In case you still want to use Arduino String, you should be able to use like
_str->add_on_state_callback([this](std::string str_n)
{ str = String(str_n); });

How to send JSON with QTcpSocket

I can send JSON object from client to server but can not get an item from JSON object on the server.
I've tried several examples that I found when I researched I can not solve the problem.
Code to send JSON:
QJsonObject levan{
{"id",1},
{"tipo","reforço"},
{"ca", 10},
{"cb",0},
{"cc",0},
{"cd",0},
{"ce",0},
{"cf",0},
{"cg",0},
{"ch",0},
};
QJsonArray jsarray {levan};
QJsonDocument jsDoc(jsarray);
QString jsString = QString::fromLatin1(jsDoc.toJson());
this->tcpSocket->write(jsString.toLatin1());
Code to receive JSON:
QString str = this->socket->readAll();
QJsonDocument jsonResponse = QJsonDocument::fromJson(str.toLatin1());
QJsonObject jsonObject = jsonResponse.object();
QJsonArray jsonArray = jsonObject["levantamento"].toArray();
qDebug()<< jsonResponse.object().value("levan");
if (jsonResponse.array().empty()) {
qDebug() << "empty";
}
I can present JSON as text, but I cannot get the items from the JSON.
You're creating a single QJsonObject and then creating a QJsonArray using that object. So, the final output is one JSON object with a nested array with only one object. I'm not sure if you intend to have only one object in array or an array of all those objects.
And, the JSON created in request is not what you're trying to read in response. There's no key levantamento or levan in your request so you cannot find anything against those in response. You need to work on population of objects in request JSON. The below example uses some of your test data for request population and extraction in response. Hope that helps!
Example:
#include <QDebug>
#include <QString>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
int main()
{
// Request: Create JSON objects and array of objects
const auto jArrKey = "levan";
const auto jTstKey = "test";
const auto jRqObj1 = QJsonObject{ { "id", 1 } };
const auto jRqObj2 = QJsonObject{ { "cb", 0 } };
const auto jRqObj3 = QJsonObject{ { "cc", 0 } };
const auto jReqArr = QJsonArray{ jRqObj1, jRqObj2, jRqObj3 };
const auto jTstObj = QJsonObject{ { "Hello", "World" } };
const auto jReqObj = QJsonObject{ { jArrKey, jReqArr }, { jTstKey, jTstObj } };
const auto jReqDoc = QJsonDocument{ jReqObj };
const auto jReqStr = QString::fromLatin1( jReqDoc.toJson() );
qDebug() << "Request:\n" << qPrintable( jReqStr );
// Response: Read JSON document and inspect its elements
const auto jRspDoc = QJsonDocument::fromJson( jReqStr.toLatin1() );
const auto jRspObj = jRspDoc.object();
const auto jRspArr = jRspObj[ jArrKey ].toArray();
const auto jRspTst = jRspObj[ jTstKey ].toObject();
qDebug() << "Response:" << jRspArr;
qDebug() << "Test Obj:" << jRspTst;
qDebug() << ">> Hello:" << jRspTst.value( "Hello" ).toString();
return 0;
}
Output:
Request:
{
"levan": [
{
"id": 1
},
{
"cb": 0
},
{
"cc": 0
}
],
"test": {
"Hello": "World"
}
}
Response: QJsonArray([{"id":1},{"cb":0},{"cc":0}])
Test Obj: QJsonObject({"Hello":"World"})
>> Hello: "World"
You put an array as the document root, so retrieve it first:
QJsonDocument jsonResponse = QJsonDocument::fromJson(str.toLatin1());
QJsonArray jsonArray = jsonResponse.array();
Check if it's empty, if not get the first item (the levan object):
if(!jsonArray.isEmpty())
{
QJsonObject jsonObject = jsonArray.first().toObject();
You can now read the object keys, e.g.:
qDebug()<< jsonObject.value("tipo");
will print:
QJsonValue(string, "reforço")
Notice that levan is not a key, but a variable name. So this line:
jsonResponse.object().value("levan");
will never work.
If the issue persists, here's a couple of hints to debug your code.
First, check for parsing errors, this way:
QJsonParseError parseError;
QJsonDocument jsonResponse = QJsonDocument::fromJson(str.toLatin1(), &parseError);
if(parseError.error != QJsonParseError::NoError)
{
qDebug() << "Parse error: " << parseError.errorString();
}
If one occurs, just inspect the string (better: inspect it anyway), to see what came in:
qDebug() << str.toLatin1().data();
The expected output is:
[
{
"ca": 10,
"cb": 0,
"cc": 0,
"cd": 0,
"ce": 0,
"cf": 0,
"cg": 0,
"ch": 0,
"id": 1,
"tipo": "reforço"
}
]

Iterate on json structure with Poco

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

AWS API Gateway - Integration Response body mapping

In AWS API Gateway Integration Response body mapping I have following code:
#set($inputRoot = $input.path('$.Item'))
[
#foreach($elem in $inputRoot.Events)
{
"id": $elem.id,
"from" : $elem.from,
"to" : $elem.to,
"spent" : $elem.spent,
#if("$!elem.comment" != "")
"comment": $elem.comment,
#end
"project" : {
"id" : $elem.project.id,
"number" : $elem.project.number,
"name" : $elem.project.name
}
}
#if($foreach.hasNext),#end
#end
]
The data comes from a lambda functions which queries a DynamoDB Table
API gateway returns the data like this:
[
{
"id": 123443214,
"from" : 19:34,
"to" : 22:30,
"spent" : 02:56,
"project" : {
"id" : 4321,
"number" : CIB,
"name" : Backend
}
}
, {
"id": 12341234,
"from" : 19:34,
"to" : 22:30,
"spent" : 02:56,
"project" : {
"id" : 12341234,
"number" : CIB,
"name" : Backend
}
}
]
So it it's already formatted. How do I get APi Gateway to return the response unformatted? So that it's just pure json, without break lines, indentations etc.?
Thanks in advance!
(Small preliminary remark: you are missing some quotes around JSON string values).
It's possible to remove line breaks using ## and indentation using #**#, as follow, but the template will look a bit ugly:
#set($inputRoot = $input.path('$.Item'))##
[##
#foreach($elem in $inputRoot.Events)##
{##
#**#"id":$elem.id,##
#**#"from": $elem.from,##
#**#"to":$elem.to,##
#**#"spent":$elem.spent,##
#if("$!elem.comment" != "")##
#* *#"comment":$elem.comment,##
#end##
#**#"project":{##
#**#"id":$elem.project.id,##
#**#"number":"$elem.project.number",##
#**#"name":"$elem.project.name"##
}##
}##
#if($foreach.hasNext),#end##
#end##
]##
Since the only reason the indentation is in here at first is readability of the template, I would go another direction.
For instance, you can add a post-processing tidy formatter in your View servlet) using org.json:
import org.json.JSONObject;
....
Writer writer = new StringWriter();
getVelocityView().merge(template, context, writer);
String compactJSON = new JSONObject(writer.toString()).toString();
response.getWriter().write(compactJSON);
But this will only work for small JSON files since the response is buffered into memory, so let's keep on searching for a more elegant solution.
The way to go is to pre-process your template, using a custom ResouceLoader.
CompactJSONResourceLoader.java
package my.custom.loader;
import java.io.InputStream;
import java.io.IOException;
import org.apache.commons.collections.ExtendedProperties;
import org.apache.velocity.exception.ResourceNotFoundException;
import org.apache.velocity.runtime.resource.Resource;
import org.apache.velocity.runtime.resource.loader.ResourceLoader;
import org.apache.velocity.runtime.resource.loader.ResourceLoaderFactory;
public class CompactJSONResourceLoader extends ResourceLoader
{
protected ResourceLoader innerLoader = null;
#Override
public void init(ExtendedProperties configuration)
{
try
{
String innerLoaderID = configuration.getString("innerLoader") + ".resource.loader";
String innerLoaderClass = rsvc.getConfiguration().getString(innerLoaderID + ".class");
innerLoader = ResourceLoaderFactory.getLoader(rsvc, innerLoaderClass);
ExtendedProperties innerConfiguration = rsvc.getConfiguration().subset(innerLoaderID);
innerLoader.commonInit(rsvc, innerConfiguration);
innerLoader.init(innerConfiguration);
}
catch (Exception e)
{
log.error("could not initialize CompactJSONResourceLoader inner loader", e);
}
}
protected class CompactJSONInputStream extends InputStream
{
InputStream innerStream = null;
boolean insideQuotes = false;
public CompactJSONInputStream(InputStream innerStream)
{
this.innerStream = innerStream;
}
#Override
public int read() throws IOException
{
int ch;
do
{
ch = innerStream.read();
if (insideQuotes)
{
if (ch == '"') insideQuotes = false;
break;
}
else if (!Character.isWhitespace(ch))
{
if (ch == '"') insideQuotes = true;
break;
}
}
while (ch != -1);
return ch;
}
}
#Override
public InputStream getResourceStream(String source) throws ResourceNotFoundException
{
return new CompactJSONInputStream(innerLoader.getResourceStream(source));
}
#Override
public boolean isSourceModified(Resource resource)
{
return innerLoader.isSourceModified(resource);
}
#Override
public long getLastModified(Resource resource)
{
return innerLoader.getLastModified(resource);
}
}
And you would then need to configure Velocity with the following properties:
resource.loader = compact
compact.resource.loader.class = my.custom.loader.CompactJSONResourceLoader
compact.resource.loader.innerLoader = file
(you would replace file with the resource loader you are currently using, of course).

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