Parser JSON ON QT [closed] - c++

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 6 years ago.
Improve this question
Is it possible to use an operator || in json like this :
{
"ven":{
"source":"logicCtrl" ,
"msg":"radio_volume" || "radio-mute", || "radio3",
"type":"int"
}
}
i can after get data by parsing data in C++ side like this :
QFile jsonFile("VenParser.json");
if (!jsonFile.open(QIODevice::ReadOnly | QIODevice::Text))
{
qDebug() << "problème d'overture du fichier, exit";
}
QByteArray jsonData = jsonFile.readAll();
QJsonParseError *err = new QJsonParseError();
QJsonDocument doc = QJsonDocument::fromJson(jsonData, err);
if (err->error != 0)
qDebug() << err->errorString();
Venparser myparser;
if (doc.isNull())
{
qDebug() << "Document invalide";
}
else if (doc.isObject())
{
//recuperer l'object json
QJsonObject jObject = doc.object();
//convertir l'object json to variantmap
QVariantMap mainMap = jObject.toVariantMap();
// variant map
QVariantMap Map = mainMap["ven"].toMap();
myparser.source = Map["source"].toString();
myparser.msg = Map["msg"].toString();
myparser.type = Map["type"].toString();
header.H file : i define my struct
struct Venparser {
QString source;
QString msg;
QString type;
My problem is that i don't want a list in my "msg" but something like this :
when i call myparser.msg , then it will check just the value i need in msg and return it.
"msg":"radio_volume" || "radio-mute", || "radio3",
Thanks,

Your example with the || token is not a valid JSON. You can read more about its format here. However, if I understand you correctly, you can easily use JSON arrays for your task.
JSON:
{
"ven": {
"source": "logicCtrl",
"msg": ["radio_volume", "radio-mute", "radio3"],
"type": "int"
}
}
C++:
You can access the msg array using the toStringList() method. Also, you can use QVariantList and toList() respectively if you fill your array with data different from strings.
QStringList messages = Map["msg"].toStringList();
Now the messages variable contains "radio_volume", "radio-mute" and "radio3" values so you can extract the required string any way you need using your code.
If you still need to parse your exact example (which is technically not a valid JSON as I said before), you will have to go with writing your own parser, which is a bit wide topic for the answer.

Related

Getting n objects and their field from JSon, then store them as class object

Im trying to get information about my objects from JSon file. It contains n objects (2 for example) 4 fields each. I parse .json by rapidjson and my IDE is Qt Creator.
I already tried using Pointers desribed at http://rapidjson.org/md_doc_pointer.html#JsonPointer and Query Objects from their basic tutorial, but somehow I can't get it working.
that's how example .json file would look.
{
"opiekun1" : {
"imie": "Maksym",
"nazwisko": "Zawrotny",
"email": "maksym#wp.pl",
"haslo": "herbatka"},
"opiekun2" : {
"imie": "Filip",
"nazwisko": "Szatkowski",
"email": "filip#wp.pl",
"haslo": "kawusia"}
}
I get DOM Document by:
FILE* fp = fopen(json_filename.c_str(), "rb");
char readBuffer[65536];
FileReadStream is(fp, readBuffer, sizeof(readBuffer));
Document d;
d.ParseStream(is);
I tried Pointer() like that:
Value* value = Pointer("/opiekun1/imie").Get(parsedJSon);
but I got:
invalid conversion from 'const rapidjson::GenericValue<rapidjson::UTF8<> >*' to 'rapidjson::Value* {aka rapidjson::GenericValue<rapidjson::UTF8<> >*}'
Mine another try was to iterate through objects in Document:
for (auto& object : parsedJSon.GetObject())
{
CUzytkownik* user;
user = new CUzytkownik;
int counter = 0;
for (Value::ConstMemberIterator itr = object.MemberBegin();
itr != object.MemberEnd(); itr++)
{
if (itr->name.GetString() == "imie")
user->imie = itr->value.GetString();
}
}
But it says:
const struct rapidjson::GenericMember<rapidjson::UTF8<>, rapidjson::MemoryPoolAllocator<> >' has no member named 'MemberEnd'
I think that I misunderstand something about handling objects in .json files. Could anyone explain it to me and provide some example code? I would like my output to look something like that:
CUzytkownik* opiekun1 = new CUzytkownik;
opiekun1->name = "Maksym";
opiekun1->nazwisko = "Zawrotny";
opiekun1->email = "maksym#wp.pl";
opiekun1->haslo = "herbatka";
If anyone has experience with rapidjson and would like to help I will be most grateful. Any alternative examples like array handling or somethng like that ale most welcome too.
Thank you in advance!

Creating json string using json lib

I am using jsonc-libjson to create a json string like below.
{ "author-details": {
"name" : "Joys of Programming",
"Number of Posts" : 10
}
}
My code looks like below
json_object *jobj = json_object_new_object();
json_object *jStr1 = json_object_new_string("Joys of Programming");
json_object *jstr2 = json_object_new_int("10");
json_object_object_add(jobj,"name", jStr1 );
json_object_object_add(jobj,"Number of Posts", jstr2 );
this gives me json string
{
"name" : "Joys of Programming",
"Number of Posts" : 10
}
How do I add the top part associated with author details?
To paraphrase an old advertisement, "libjson users would rather fight than switch."
At least I assume you must like fighting with the library. Using nlohmann's JSON library, you could use code like this:
nlohmann::json j {
{ "author-details", {
{ "name", "Joys of Programming" },
{ "Number of Posts", 10 }
}
}
};
At least to me, this seems somewhat simpler and more readable.
Parsing is about equally straightforward. For example, let's assume we had a file named somefile.json that contained the JSON data shown above. To read and parse it, we could do something like this:
nlohmann::json j;
std::ifstream in("somefile.json");
in >> j; // Read the file and parse it into a json object
// Let's start by retrieving and printing the name.
std::cout << j["author-details"]["name"];
Or, let's assume we found a post, so we want to increment the count of posts. This is one place that things get...less tasteful--we can't increment the value as directly as we'd like; we have to obtain the value, add one, then assign the result (like we would in lesser languages that lack ++):
j["author-details"]["Number of Posts"] = j["author-details"]["Number of Posts"] + 1;
Then we want to write out the result. If we want it "dense" (e.g., we're going to transmit it over a network for some other machine to read it) we can just use <<:
somestream << j;
On the other hand, we might want to pretty-print it so a person can read it more easily. The library respects the width we set with setw, so to have it print out indented with 4-column tab stops, we can do:
somestream << std::setw(4) << j;
Create a new JSON object and add the one you already created as a child.
Just insert code like this after what you've already written:
json_object* root = json_object_new_object();
json_object_object_add(root, "author-details", jobj); // This is the same "jobj" as original code snippet.
Based on the comment from Dominic, I was able to figure out the correct answer.
json_object *jobj = json_object_new_object();
json_object* root = json_object_new_object();
json_object_object_add(jobj, "author-details", root);
json_object *jStr1 = json_object_new_string("Joys of Programming");
json_object *jstr2 = json_object_new_int(10);
json_object_object_add(root,"name", jStr1 );
json_object_object_add(root,"Number of Posts", jstr2 );

C++ json deserializer [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
We don’t allow questions seeking recommendations for books, tools, software libraries, and more. You can edit the question so it can be answered with facts and citations.
Closed 7 years ago.
Improve this question
We can C++ project and we need to (de) serialize objects from and into json.
In C# we are using JSON.NET. We simple call:
string json = JsonConvert.SerializeObject(product);
var myNewObject = JsonConvert.DeserializeObject<MyClass>(json);
Very simple and useful.
Does anybody know about free C++ library, which can be used in the same simple way like in C#?
We are using JsonCpp, but it does not support it.
Thanks very much
Regards
C++ does not support reflection so you necessarily have to write your own serialize and deserialize functions for each object.
I'm using https://github.com/nlohmann/json in a C++ websocket server talking to a html/javascript client. The websocket framework is https://github.com/zaphoyd/websocketpp. So sending the json structure 'matches' from the server goes like
msg->set_payload(matches.dump());
m_server.send(hdl, msg);
And like wise from the client
var m = "la_liga";
var msg = {
"type": "request",
"data": m
}
msg = JSON.stringify(msg);
ws.send(msg);
When I receive json on the server I parse it and then a try-catch
void on_message(connection_hdl hdl, server::message_ptr msg) {
connection_ptr con = m_server.get_con_from_hdl(hdl);
nlohmann::json jdata;
std::string payload = msg->get_payload();
try {
jdata.clear();
jdata = nlohmann::json::parse(payload);
if (jdata["type"] == "update") {
<do something with this json structure>
}
} catch (const std::exception& e) {
msg->set_payload("Unable to parse json");
m_server.send(hdl, msg);
std::cerr << "Unable to parse json: " << e.what() << std::endl;
}
}
And likewise on the client
ws.onmessage = function (e) {
var receivedMsg = JSON.parse(e.data);
if (receivedMsg.type == "table") {
<sort and display updated table standing>
}
}
Websocketpp requires boost libraries.
I wrote this serializer/deserializer for c++ since I couldn't find any non boost serializer that fit my needs:
Pakal Persist
It supports both json and xml and polymorphics objets as well.

JSON parsing with Qt

Hei , I have this text in a JSON :
( without the returns all in one line)
[
{
"ERROR":false,
"USERNAME":"Benutzer",
"FORMAT":"HUMAN",
"LATITUDE_MIN":84,
"LATITUDE_MAX":36,
"LONGITUDE_MIN":5,
"LONGITUDE_MAX":20,
"RECORDS":203
},
[
{
"MMSI":233434540,
"TIME":"2014-10-09 06:19:06 GMT",
"LONGITUDE":8.86037,
"LATITUDE":54.12666,
"COG":347,
"SOG":0,
"HEADING":236,
"NAVSTAT":0,
"IMO":0,
"NAME":"HELGOLAND",
"CALLSIGN":"DK6068",
"TYPE":90,
"A":20,
"B":15,
"C":4,
"D":4,
"DRAUGHT":2,
"DEST":"BREMERHAVEN",
"ETA":"00-00 00:00"
},
{
"MMSI":319072300,
"TIME":"2014-10-09 06:08:53 GMT",
"LONGITUDE":9.71578,
"LATITUDE":54.31949,
"COG":343.6,
"SOG":0,
"HEADING":197,
"NAVSTAT":5,
"IMO":1012189,
"NAME":"M.Y. ESTER III",
"CALLSIGN":"ZGED3",
"TYPE":37,
"A":31,
"B":35,
"C":7,
"D":6,
"DRAUGHT":3.5,
"DEST":"SCHACT AUDORF",
"ETA":"09-16 08:00"
}
// many more lines but the Json IS VALID.
]
]
I would parse it and put that in a MYSQL table.
Not all, only name and MMSI first.
But this don't view anything in my consle because its dont jump in the foreach:
bool ok = true;
// my json data is in reply & ok is a boolean
QVariantList result = parser.parse(reply, &ok).toList();
foreach(QVariant record, result) {
QVariantMap map = record.toMap();
qDebug() << map.value("NAME");
}
What's wrong ?
When i debug, i only see that it doesn't jump in the foreach.
I use the QJson libary : QJson::Parser parser; But please anyone can tell me what i do wrong?
Your code looks like you are iterating over top level array, while the data you are looking for is in the nested array, which is effectively the second item of the top level array. So, you need to iterate over items in the inner array.
The following code works for me with your sample JSON:
QVariantList result = parser.parse(reply, &ok).toList().at(1).toList();
foreach (const QVariant &item, result) {
QVariantMap map = item.toMap();
qDebug() << map["NAME"].toString();
qDebug() << map["MMSI"].toLongLong();
}
If you are using Qt5 or above, you can make use of the awesome features provided by QJsonDocument, QJsonObject and QJsonArray.
I have copied your json into a file named test.txt in my D-drive and the code below works fine.
QJsonDocument jsonDoc;
QByteArray temp;
QFile file("D://test.txt");
if (file.open(QIODevice::ReadOnly | QIODevice::Text))
{
temp = file.readAll();
}
jsonDoc = QJsonDocument::fromJson(temp);
QJsonArray jsonArray = jsonDoc.array().at(1).toArray(); //Since you are interested in the json array which is the second item and not the first json object with error
for(int i =0; i < jsonArray.size(); ++i)
{
QJsonObject jsonObj = jsonArray.at(i).toObject();
int mmsi = jsonObj.find("MMSI").value().toInt();
QString name = jsonObj.find("NAME").value().toString();
qDebug() << mmsi;
qDebug() << name;
}
If you have to stick to Qt4, you can try using the qjson4 library which tries to mimic the behavior of the 'QJsonDocument' which is part of Qt5.

Extract data from CSV with Regex and convert it to JSON

Imagine you have a table in a CSV file with this kind of layout:
name,property1 [unit1],property2 [unit2]
name1,4.5,2.3
name2,3.2,7.4
name3,5.5,6.1
I need to convert each row to this kind of JSON structure (ie, for row 1):
{
"name1": [
{
"properties": [
{
"property_1": "_value_",
"unit": "unit1"
},
{
"property_2": "_value_",
"unit": "unit2"
}
]
}
]
}
On top of it all, I have to explain that I am using Qt 4.7 and can't update; also, I can't install Qxt so I'm relying on qt-json for the JSON parsing/encoding. More, the CSV file is not created/maintained by me, so I can't really change it either.
So with all of this, I realised I need a few things, so this is a kind of multiple question:
how should I write the RegEx to read the unit in each column's header? Please note that the unit is enclosed in rect-parenthesis.
imagine I extract both the header row and the other rows into a QList<QString>, separating each column as a string. How can I manage to sync all the bits of data in order to create the JSON structure I need on a QString? (I think I need it in a QString so I can dump each row in a different file, but I'm open to other options as well)
Just one final note - I also need to this to be somewhat scalable. The CSV files on which this will be apllied are very heterogenous in column count: some have 8 columns, others have 20.
I know it is not a good practice to post "multiquestions", but the thing is I'm feeling too overwhelmed with all of this, and because I have virtually no experience with Qt, I can't even define a plan to attack this. Hope someone can share some pointers. Thanks!
EDIT
So, I've been thinking a little more about this and I don't actually know if this is a good idea/feasible but here is what I thought of:
when going through the header row, I would check if each column string had a hit for the RegEx. If so, I would store the column index and the unit string in a list;
then, when going through the other rows, in order to parse them into JSON, I would check in each column if it matched the index in the previous list, and if so, I would then add the unit to the map (as qt-json docs explains)
Does this make any sense? Can anyone mock up a skeleton I can work on for this?
EDIT2
I've managed to get a few things working so far, but still not working as it should. Right now I have managed to read properly from the CSV file, but the output isn't coming out right. Can anyone share some insight?
NOTE: the processLineFromCSV function returns a QStringList obtained like so: QStringList cells = line.split(separator_char);
NOTE2: the RegEx was obtained from this answer.
NOTE3: Check below for the type of output I'm getting. Right now I think the problem relates more to the usage of the qt-json lib than actually the rest of the code, but any help is welcome! :)
The code so far:
QFile file(csvfile);
if (file.open(QIODevice::ReadOnly | QIODevice::Text))
{
bool first = true;
QVariantMap map;
QVariantMap propertyMap;
QList<QVariant> generalList, propertiesList;
while (!file.atEnd())
{
QString line = file.readLine();
if(first == true){
headerList = processLineFromCSV(line, separator_char);
first = false;
}else{
QStringList cellList = processLineFromCSV(line, separator_char);
int i=0;
for(i; i<cellList.size(); i++)
{
// check the header cell for "[unit]" string
// returns -1 if does not have the string
// if it has the string, it's stored in capturedUnits[1]
int test = exp.indexIn(headerList.at(i));
// store the captured units in a QStringList
QStringList capturedUnits = exp.capturedTexts();
if(test==-1){ // if header does not have a captured unit - general column
QString name = headerList.at(i);
QString sanitizeName= name.remove(exp.capturedTexts().at(0), Qt::CaseSensitive);
map[sanitizeName] = cellList.at(i);
}
else{ // if header string has a captured unit - property column
QString propertyName = headerList.at(i); // extract string in header
QString sanitizedPropertyName = propertyName.remove(exp); //remove the unit regex from the string
sanitizedPropertyName.remove(QChar('\n'), Qt::CaseSensitive); // clear newlines
if(sanitizedPropertyName.startsWith('"') && sanitizedPropertyName.endsWith('"'))
{
sanitizedPropertyName.remove(0,1);
sanitizedPropertyName.remove(sanitizedPropertyName.length(),1);
}
QString value =cellList.at(i); // extract string in value
QString sanitizedValue = value.remove(QChar('\n'), Qt::CaseSensitive); // clear newlines
if(sanitizedValue.startsWith('"') && sanitizedValue.endsWith('"'))
{
sanitizedValue.remove(0,1);
sanitizedValue.remove(sanitizedValue.length(),1);
}
propertyMap[sanitizedPropertyName]= sanitizedValue; // map the property: value pair
propertyMap["unit"] = capturedUnits.at(1); // map the unit: [unit] value pair
QByteArray general = QtJson::serialize(map); // serialize the pair for general column
QByteArray properties = QtJson::serialize(propertyMap); // serialize the pair for property column
QVariant genVar(general);
QVariant propVar(properties);
generalList.append(genVar);
propertiesList.append(propVar);
}
}
}}
QByteArray finalGeneral = QtJson::serialize(generalList);
QByteArray finalProperties = QtJson::serialize(propertiesList);
qDebug() << finalGeneral;
qDebug() << finalProperties;
file.close();
}
The ouput:
"[
"{ \"name\" : \"name1\" }",
"{ \"name\" : \"name1\" }",
"{ \"name\" : \"name2\" }",
"{ \"name\" : \"name2\" }",
"{ \"name\" : \"name3\" }",
"{ \"name\" : \"name3\" }"
]"
"[
"{ \"property1 \" : \"4.5\", \"unit\" : \"unit1\" }",
"{ \"property1 \" : \"4.5\", \"property2 \" : \"2.3\", \"unit\" : \"unit2\" }",
"{ \"property1 \" : \"3.2\", \"property2 \" : \"2.3\", \"unit\" : \"unit1\" }",
"{ \"property1 \" : \"3.2\", \"property2 \" : \"7.4\", \"unit\" : \"unit2\" }",
"{ \"property1 \" : \"5.5\", \"property2 \" : \"7.4\", \"unit\" : \"unit1\" }",
"{ \"property1 \" : \"5.5\", \"property2 \" : \"6.1\", \"unit\" : \"unit2\" }"
]"
This should be a good start for you:
QString csv = "name,property1 [unit1],property2 [unit2],property3 [unit3]\n"
"name1,4.5,2.3\n"
"name2,3.2,7.4\n"
"name3,5.5,6.1,4.3\n";
QStringList csvRows = csv.split('\n', QString::SkipEmptyParts);
QStringList csvHeader = csvRows.takeFirst().split(',');
csvHeader.removeFirst();
foreach(QString row, csvRows) {
QStringList values = row.split(',');
QString rowName = values.takeFirst();
QVariantList properties;
for(int i = 0; i < values.size(); i++) {
QString value = values[i];
QStringList propParts = csvHeader[i].split(' ');
QString propName = propParts[0];
QString propType = propParts[1].mid(1, propParts[1].size() - 2);
QVariantMap property;
property[propName] = value;
property["unit"] = propType;
properties.append(property);
}
QVariantMap propertyObj;
propertyObj["properties"] = properties;
QVariantList propList;
propList.append(propertyObj);
QVariantMap root;
root[rowName] = propList;
QByteArray json = QtJson::serialize(root);
qDebug() << json;
// Now you can save json to a file
}
Joum.
Just seen your response to my comment. I don't have much experience with QT either, but a quick outline....
Extract the data one line at a time, and 'split' it into an array. If you are using CSV you need to be sure that there are no data points that have a comma in them, or the split will result in a real mess. Check with whoever extracted the data if they can use another 'less common' separator (eg a '|' is good). if you data is all numeric that is great, but be wary of locations that use the comma as a decimal separator :(
I hope that you have 1 'table' per file, if not you need to be able to 'identify' when a new table starts somehow, this could be interesting / fun - depends on your outlook ;).
At the end you will have a collection of 'string arrays' (a table of some sort) hopefully the first is your header info. If you have mutliple tables, you will deal with them one at a time
You should now be able to 'output' each table in good JSON format.
Getting your 'units' from the header rows: If you know in advance where they are located (ie the index in the array) you can plan for extracting the info (using a regex if you wish) in the correct index locations.
Last point.
If your csv file is very long (hundreds of lines), just grab the first few into a new test file for quicker debuging, then once you are happy, enlarge it a bit and check the output format... then again once you are happy that there are no other bugs... for the whole file
Likewise if you have multiple tables in your file, start with the first one only, then add the first part of a second... test.... add a third.... test etc etc etc until you are happy
David.
A possibly better solution, after reading your comment about wanting some form of 'synchronisation'.
NOTE: this may seem a little more complex, but I think it would be a more flexible solution in the end. Also does this data not exist in a DB somewhere (who gave it to you?), can they give you direct read access to the underlying DB and tables? if so, you can jump straight to the 'output each table to JSON' step.
using an embeded DB (ie SQLite).
Extract the first 'header' row, and create a table in your DB that follows the info there (you should be able to add info regarding units to the 'metadata' ie a description). If all your files are the same you could even import all the data into the same single table, or auto create a new table (assuming the same format) for each new file using the same create table statement.
I'm sure there is a 'csvimport' in SQLite (I haven't checked the docs yet, and haven't done this in a while) or someone has written a library that will do this.
Output each table to JSON format, again I'm sure someone has written a library for this.
Using the answer by ExplodingRat this is the final code: (without file creation at the end)
QString csvfile = ui->lineEditCSVfile->text();
QString separator_char = ui->lineEditSeparator->text();
QRegExp exp("\\[([^\\]]+)\\]");
QFile file(csvfile);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
return;
QString csv = file.readAll();
QStringList csvRows = csv.split('\n', QString::SkipEmptyParts);
QStringList csvHeader = csvRows.takeFirst().split(separator_char);
csvHeader.removeFirst();
foreach(QString row, csvRows) {
QStringList values = row.split(separator_char);
QString rowName = values.takeFirst();
QVariantList general;
QVariantList properties;
for(int i = 0; i < values.size(); i++) {
QString value = values[i];
int test = exp.indexIn(csvHeader[i]);
//qDebug() << test;
//qDebug() << csvHeader;
QStringList capturedUnits = exp.capturedTexts();
QString propName = csvHeader[i];
if(test==-1){
//QString propName = csvHeader[i].remove(exp);
//qDebug() <<"property name" << propName;
QVariantMap property;
property[propName] = value;
general.append(property);
}else{
propName.remove(exp);
//QStringList propParts = csvHeader[i].split(' ');
//QString propName = csvHeader[i].remove(exp);
QString propType = capturedUnits[1];
QVariantMap property;
property[propName] = value;
property["unit"] = propType;
properties.append(property);
}
}
QVariantMap propertyObj;
propertyObj["properties"] = properties;
QVariantList propList;
propList.append(propertyObj);
QVariantMap generalObj;
generalObj["general"] = general;
QVariantList generalList;
generalList.append(generalObj);
QVariantList fullList;
fullList.append(generalObj);
fullList.append(propertyObj);
QVariantMap root;
root[rowName] = fullList;
QByteArray json = QtJson::serialize(root);
json.prepend('[');
json.append(']');
qDebug() << json;
// Now you can save json to a file