QJsonDocument::fromJson cannot parse QJsonDocument::toJson QByteArray - c++

I'm using QJsonDocument::toJson to convert a QJsonObject into a QByteArray, and somewhere else in the program, I read that QByteArray and convert it to a QJsonObject using QJsonDocument::fromJson. It doesn't work and I don't know why. QJsonParseError doesn't show anything.
How can I successfully convert to and from QByteArray <--> QJsonObject?
If I run:
QJsonDocument doc, doc2;
QJsonParseError jsonerror;
QJsonObject original, copy;
original.insert("foo", 1);
original.insert("bar",2);
doc.setObject(original);
doc2.fromJson(doc.toJson(), &jsonerror);
copy = doc2.object();
qDebug() << doc.toJson();
qDebug() << jsonerror.errorString();
qDebug() << "Null: " << doc2.isNull() << " Object: " << doc2.isObject() << " Array: " << doc2.isArray() << " Empty: " << doc2.isEmpty();
qDebug() << copy.size();
I get the following output:
"{\n "bar": 2,\n "foo": 1\n}\n"
"no error occurred"
Null: true Object: false Array: false Empty: true
0
I expected the object copy to contain the key-value pairs from original. It seems like fromJson is not managing to read the result of toJson.
I also tried using the format identifier in toJson to force it to be compact doc2.fromJson(doc.toJson(QJsonDocument::Compact), &jsonerror); and while the output is different, the resulting object is still empty
"{"bar":2,"foo":1}"
"no error occurred"
Null: true Object: false Array: false Empty: true
0

The method fromJson is a static member function, so calling doc2.fromJson(...) does not change anything inside doc2, instead it constructs a new QJsonDocument and as it has nowhere to go (it is not assigned to anything) it is lost.
This is the example above corrected:
QJsonDocument doc;
QJsonParseError jsonerror;
QJsonObject original, copy;
original.insert("foo", 1);
original.insert("bar",2);
doc.setObject(original);
QJsonDocument doc2 = QJsonDocument::fromJson(doc.toJson(), &jsonerror);
copy = doc2.object();
qDebug() << doc.toJson();
qDebug() << jsonerror.errorString();
qDebug() << "Null: " << doc2.isNull() << " Object: " << doc2.isObject() << " Array: " << doc2.isArray() << " Empty: " << doc2.isEmpty();
qDebug() << copy.size();
Which generates the output:
"{\n "bar": 2,\n "foo": 1\n}\n"
"no error occurred"
Null: false Object: true Array: false Empty: false
2

Related

Parse nested JSON with QJsonDocument in Qt

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

Value disappears from QVector

I have the QMap with QVector inside:
QMap<QString, QMap<QGraphicsView*, QVector<float>>> graphs;
I'm trying to execute this code:
QVector<float>* graphValues = &(graphs.values()[i - values.count()].values()[0]);
graphValues->push_back(1234);
qDebug() << "=== Debug messages ===\r\n";
qDebug() << "i - values.count() = " << i - values.count();
qDebug() << "graphValues = " << graphValues;
qDebug() << "*graphValues = " << *graphValues;
qDebug() << "graphs = " << graphs;
qDebug() << "graphs.values()[i - values.count()].values()[0] = " << graphs.values()[i - values.count()].values()[0];
It gives me output:
i - values.count() = 0
graphValues = 0x2000e90
*graphValues = QVector(1234)
graphs = QMap(("tempgraph", QMap((QGraphicsView(0x1fb1920) , QVector() ) ) ))
graphs.values()[i - values.count()].values()[0] = QVector()
So, where is my value? Using pointer i wait for my value in graphs, but it disappears.
QMap::values() method returns temporary object. You cannot change original data, by changing it. You should use iterators, or QMap::operator[] somehow.

TinyXML2 EXC_BAD_ACCESS Im sure its a null ptr but no idea why

So I have been at this for days, and I have no idea why a BAD_ACCESS error is thrown. Sometimes it works, sometimes it doesn't.
void xmlParser::parseXML(string file){
tinyxml2::XMLDocument doc;
if(!doc.LoadFile(file.c_str()))
{
cout << "ERROR: TINYXML2 FAILED TO LOAD" << endl;
}
//XML FILE LAYOUT:
//<item>
// <type id="laserWeapon" name="Laser Rifle">
// <tooltip>
// <stats>
//</item>
//error seems to occur on this line
tinyxml2::XMLElement* elementType = doc.FirstChildElement("item")->FirstChildElement("type");
string id = elementType->Attribute("id");
string name = elementType->Attribute("name");
cout << "id: " << id << endl;
cout << "name: " << name << endl;
}
I use
xmlparser.parseXML(xmlparser.path+"laserRifle.xml");
to load the file. Should I be parsing this as a string, or is there some null ptr I'm neglecting? I've tried to do an 'if nullptr' clause, but it still turns out an error instead of skipping over it.
Any advice on what to do? I'm completely lost with this.
// item element can be missed and you'll get bad access. Do not chain your calls that way
tinyxml2::XMLElement* elementType = doc.FirstChildElement("item")->FirstChildElement("type");
// element type can be missed, as well as attributes id and name
string id = elementType->Attribute("id");
string name = elementType->Attribute("name");
cout << "id: " << id << endl;
cout << "name: " << name << endl;
}
Carefully check every element and attribute. Do not chain calls because every call can return null. If you check all nullptr cases you'll find your error

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.

How can I extract the displayed text from a QTableWidgetItem?

I have a subclass of QTableWidget with the following code:
connect(this, SIGNAL(cellChanged(int, int)), this, SLOT(pushCellChange(int, int)), Qt::QueuedConnection);
...
void MyTableView::pushCellChange(int row, int column)
{
QString text(item(row, column)->text());
QByteArray data = text.toAscii();
cout << data.length() << endl;
const char* cellData = text.toAscii().constData();
cout << "Cell ("<<row<<", "<<column<<") changed to: " << cellData << endl;
}
When I change the upper-right cell to anything this outputs:
2
Cell (0, 0) changed to: ▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌░▬∟C▌▌
However, while this corrupt data is spewed out on the console, the table widget itself seems to behave normally and shows the correct string. Does anyone know what is going on here?
The call toAscii() is storing the QString's data to a QByteArray. In your code, you do this twice:
QByteArray data = text.toAscii();
const char* cellData = text.toAscii().constData();
_____________^ <-- temporary QByteArray
The const char* is actually pointing to the data within a temporary variable, which goes out of scope at the semicolon, at which point the pointer becomes invalid. If instead you were to make use of the local variable data, you'd be OK:
const char* cellData = data.constData();
___^ <-- still-in-scope QByteArray
Alternatively, you can do this all in-line with the cout and the data will still be valid when it is copied to the output stream:
cout << "Cell ("<<row<<","<<column<<") changed to: " << text.toAscii().constData() << endl;
std::string cellData = text.ToStdString();
cout << "Cell ("<<row<<", "<<column<<") changed to: " << cellData << endl;
That should work fine. As for why toAscii doesn't work, I have no clue.
If it's just about the console output, you could also use qDebug() (available after #include <QDebug>) and pass the QString directly:
void MyTableView::pushCellChange(int row, int column)
{
qDebug() << item(row, column)->text().length();
qDebug() << "Cell (" << row << ", " << column << ") changed to: "
<< item(row, column)->text();
}
This way, you don't have to mess with data conversion …