QT5 C++ QByteArray XML Parser - c++

I get the following xml
<Tra Type="SomeText">
<tr>Abcdefghij qwertzu</tr>
<Rr X="0.0000" Y="0.0000" Z="0.0000" A="0.0000" B="0.0000" C="0.0000" />
<Ar A1="0.0000" A2="0.0000" A3="0.0000" A4="0.0000" A5="0.0000" A6="0.0000" />
<Er E1="0.0000" E2="0.0000" E3="0.0000" E4="0.0000" E5="0.0000" E6="0.0000" />
<Te T21="1.09" T22="2.08" T23="3.07" T24="4.06" T25="5.05" T26="6.04" T27="7.03" T28="8.02" T29="9.01" T210="10.00" />
<D>125</D>
<IP></IP>
</Tra>
through a socket that saves it in a QByteArray called Data.
I want to extract and save every value from the xml to different variables (some as Integers some as QString's).
My main problem is that I dont know how to distinguish xml strings like <D>125</D> with a value in between the Tags and xml strings like <Te T210="10.00" T29="9... /> that got the value in the Tag-String itself.
My code looks like this so far:
QByteArray Data = socket->readAll();
QXmlStreamReader xml(Data);
while(!xml.atEnd() && !xml.hasError())
{
.....
}

There's just so many examples already, aren't there? =(
Anyway, like Frank said, if you want to read data (characters) from within tags - use QXmlStreamReader::readElementText.
Alternatively, you can do this:
QXmlStreamReader reader(xml);
while(!reader.atEnd())
{
if(reader.isStartElement())
{
if(reader.name() == "tr")
{
reader.readNext();
if(reader.atEnd())
break;
if(reader.isCharacters())
{
// Here is the text that is contained within <tr>
QString text = reader.text().toString();
}
}
}
reader.readNext();
}
For attributes, you should use QXmlStreamReader::attributes which will give you a container-type class of attributes.
QXmlStreamReader reader(xml);
while(!reader.atEnd())
{
if(reader.isStartElement())
{
if(reader.name() == "Rr")
{
QXmlStreamAttributes attributes = reader.attributes();
// This doesn't check if the attribute exists... just a warning.
QString x = attributes.value("X").toString();
QString y = attributes.value("Y").toString();
QString a = attributes.value("A").toString();
// etc...
}
}
reader.readNext();
}

Related

How to convert xml node data into string in QT [duplicate]

This question already has an answer here:
Reading an XML file using QXmlStreamReader
(1 answer)
Closed 6 years ago.
I have xml file in my local machine. The xml file format is like:
<string>
<Data>
<Name>Sanket</Name>
<Number>0987654321</Number>
<Address>India</Address>
</Data>
<Data>
<Name>Rahul</Name>
<Number>0987654321</Number>
<Address>Maharashtra</Address>
</Data>
</string>
I want to convert this XML file data into String format. Like:
Sanket 0987654321 India
Rahul 0987654321 Maharashtra
What is the easiest way to convert this data in QT using c++.
I am new in that, so please can anyone suggest me some sample code for this?
Thank you in advance.
I tried following code, but that not work for me:
void parseFile()
{
QList<QList<QString> > dataSet;
QString lastError = "";
QFile inFile("test.xml");
if (inFile.open(QIODevice::ReadOnly))
{
QTextStream fread(&inFile);
long totalSize = inFile.size();
QString line;
while(!fread.atEnd())
{
line = fread.readLine();
QList<QString> record = line.split(QString::KeepEmptyParts);
dataSet.append(record);
}
qDebug()<<dataSet;
}else{
lastError = "Could not open "+test.xml+" for reading";
}
}
You could parse the xml elements firstly via QXmlStreamReader and then you can assemble the xml elements into the string how you want.
The problem of you Code is that you only process the text Lines without any xml-syntax processed by the xml class.
You should look at the QtXML classes for which Florent Uguet provided some links.
However I modified the example found here to do what you want (It does that exact thing for your exact input):
#include <QDomDocument>
#include <QFile>
#include <iostream>
#include <QDomNodeList>
int main()
{
QDomDocument doc("mydocument");
QFile file("test.xml");
if (!file.open(QIODevice::ReadOnly))
return 1;
if (!doc.setContent(&file)) {
file.close();
return 1;
}
file.close();
const auto stringTags = doc.elementsByTagName("string");
for(int stringsI = 0; stringsI < stringTags.size(); ++stringsI){
QDomNode stringTag = stringTags.at(stringsI);
for(QDomNode dataTag = stringTag.firstChildElement("Data"); !dataTag.isNull(); dataTag = dataTag.nextSiblingElement("Data")){
for(QDomNode innerTag = dataTag.firstChild(); !innerTag.isNull(); innerTag = innerTag.nextSibling()){
auto val = innerTag.toElement().text();
std::cout << val.toStdString() << " ";
}
std::cout << std::endl;
}
}
return 0;
}
I build it with QtCreator using qmake. For this you should know that you need to put QT += xml in your *.pro file.
Already asked (and with code) : Reading an XML file using QXmlStreamReader
Qt provides a set of classes for handling XML :
http://doc.qt.io/qt-5.7/qtxml-index.html
http://doc.qt.io/qt-5.7/qxmlstreamreader.html
http://doc.qt.io/qt-5.7/qxmlstreamwriter.html
Old C++ classes (not maintained)
http://doc.qt.io/qt-5/qtxml-module.html
Once you have parsed your file using these, you can usually read the individual nodes' inner text or attributes.

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.

Read XML node with RapidXML

I'm using RapidXML to parse XML files and read nodes content but I don't want to read values inside a node, I need to read the content of specific XML nodes "as XML" not as parsed values.
Example :
<node1>
<a_lot_of_xml>
< .... >
</a_lot_of_xml>
</node1>
I need to get the content of node1 as :
<a_lot_of_xml>
< .... >
</a_lot_of_xml>
What I tired :
I tried something but its not really good in my opinion, its about to put in node1, the path of an other xml file to read, I did like this :
<file1ToRead>MyFile.xml</file1ToRead>
And then my c++ code is the following :
ifstream file(FileToRead);
stringstream buffer; buffer << file.rdbuf();
But the problem is users will have a lot of XML files to maintain and I just want to use one xml file.
I think "a lot of XML files" is a better way, so you have a directory of all xml files, you can read the xml file when you need it, good for performance.
Back to the problem, can use the rapidxml::print function to get the xml format.
bool test_analyze_xml(const std::string& xml_path)
{
try
{
rapidxml::file<> f_doc(xml_path.c_str());
rapidxml::xml_document<> xml_doc;
xml_doc.parse<0>(const_cast<char*>(f_doc.data()));
rapidxml::xml_node<>* node_1 = xml_doc.first_node("node1");
if(node_1 == NULL)
{
return false;
}
rapidxml::xml_node<>* plain_txt = node_1->first_node("a_lot_of_xml");
if (plain_txt == NULL)
{
return false;
}
std::string xml_data;
rapidxml::print(std::back_inserter(xml_data), *plain_txt, rapidxml::print_no_indenting); //the xml_data is XML format.
}
catch (...)
{
return false;
}
return true;
}
I'm unfamiliar with rapidxml, but I have done this with tinyxml2. The trick is to read out node1 and then create a new XMLDoc (using tinyxml2 terms here) that contains everything inside of node1. From there, you can use their XMLPrinter class to convert your new XMLDoc (containing everything in node1) to a string.
tinyxml2 is a free download.

Parsin XML file using pugixml

Hi
I want to use XML file as a config file, from which I will read parameters for my application. I came across on PugiXML library, however I have problem with getting values of attributes.
My XML file looks like that
<?xml version="1.0"?>
<settings>
<deltaDistance> </deltaDistance>
<deltaConvergence>0.25 </deltaConvergence>
<deltaMerging>1.0 </deltaMerging>
<m> 2</m>
<multiplicativeFactor>0.7 </multiplicativeFactor>
<rhoGood> 0.7 </rhoGood>
<rhoMin>0.3 </rhoMin>
<rhoSelect>0.6 </rhoSelect>
<stuckProbability>0.2 </stuckProbability>
<zoneOfInfluenceMin>2.25 </zoneOfInfluenceMin>
</settings>
To pare XML file I use this code
void ReadConfig(char* file)
{
pugi::xml_document doc;
if (!doc.load_file(file)) return false;
pugi::xml_node tools = doc.child("settings");
//[code_traverse_iter
for (pugi::xml_node_iterator it = tools.begin(); it != tools.end(); ++it)
{
cout<<it->name() << " " << it->attribute(it->name()).as_double();
}
}
and I also was trying to use this
void ReadConfig(char* file)
{
pugi::xml_document doc;
if (!doc.load_file(file)) return false;
pugi::xml_node tools = doc.child("settings");
//[code_traverse_iter
for (pugi::xml_node_iterator it = tools.begin(); it != tools.end(); ++it)
{
cout<<it->name() << " " << it->value();
}
}
Attributes are loaded corectly , however all values are equals 0. Could somebody tell me what I do wrong ?
I think your problem is that you're expecting the value to be stored in the node itself, but it's really in a CHILD text node. A quick scan of the documentation showed that you might need
it->child_value()
instead of
it->value()
Are you trying to get all the attributes for a given node or do you want to get the attributes by name?
For the first case, you should be able to use this code:
unsigned int numAttributes = node.attributes();
for (unsigned int nAttribute = 0; nAttribute < numAtributes; ++nAttribute)
{
pug::xml_attribute attrib = node.attribute(nAttribute);
if (!attrib.empty())
{
// process here
}
}
For the second case:
LPCTSTR GetAttribute(pug::xml_node & node, LPCTSTR szAttribName)
{
if (szAttribName == NULL)
return NULL;
pug::xml_attribute attrib = node.attribute(szAttribName);
if (attrib.empty())
return NULL; // or empty string
return attrib.value();
}
If you want stock plain text data into the nodes like
<name> My Name</name>
You need to make it like
rootNode.append_child("name").append_child(node_pcdata).set_value("My name");
If you want to store datatypes, you need to set an attribute. I think what you want is to be able to read the value directly right?
When you are writing the node,
rootNode.append_child("version").append_attribute("value").set_value(0.11)
When you want to read it,
rootNode.child("version").attribute("version").as_double()
At least that's my way of doing it!

Parsing <multi_path literal="not_measured"/> in TinyXML

How do I parse the following in TinyXML:
<multi_path literal="not_measured"/>
I am able to easily parse the below line:
<hello>1234</hello>
The problem is that the first statement is not getting parsed the normal way. Please suggest how to go about this.
Not 100% sure what youre question is asking but here is a basic format too loop through XML files using tinyXML:
/*XML format typically goes like this:
<Value atribute = 'attributeName' >
Text
</value>
*/
TiXmlDocument doc("document.xml");
bool loadOkay = doc.LoadFile(); // Error checking in case file is missing
if(loadOkay)
{
TiXmlElement *pRoot = doc.RootElement();
TiXmlElement *element = pRoot->FirstChildElement();
while(element)
{
string value = firstChild->Value(); //Gets the Value
string attribute = firstChild->Attribute("attribute"); //Gets the attribute
string text = firstChild->GetText(); //Gets the text
element = element->NextSiblingElement();
}
}
else
{
//Error conditions
}