QXmlQuery no result - qt

Using Qt 4.8.4 on Windows platform and trying to use QXmlQuery without any luck. Obviously researched this topic without finding a solution.
Here is the xml file
<?xml version="1.0" encoding="UTF-8"?>
<root>
<a>abc</a>
<a>def</a>
<a>123</a>
</root>
The code looks like this;
QFile temp("C:/Temp/data.xml");
bool opened = temp.open(QIODevice::ReadOnly | QIODevice::Text);
QXmlQuery q;
q.bindVariable("file", &temp);
q.setQuery("declare variable $file external;doc($file)//root");
bool valid = q.isValid();
QStringList items;
q.evaluateTo(&items);
int len = items.size();
File is opened successfully, QXmlQuery is valid but QStringList contains 0 items. Why does not the query return a result?

Changing the XQuery from
q.setQuery("declare variable $file external;doc($file)//root");
to
q.setQuery("declare variable $file external;doc($file)//root/string()");
solved the problem.
The docs did actually state "The query must evaluate to a sequence of xs:string values. If the query does not evaluate to a sequence of strings, the values can often be converted by adding a call to string() at the end of the XQuery."
http://qt-project.org/doc/qt-4.8/qxmlquery.html#evaluateTo-3

Related

Encoding a QString in JSON

I'm trying to encode a QString into a JSON string, so that I can inject it safely via QWebFrame::evaluateJavaScript(QString("o.text = %1;").arg(???)).
For example, in php using the function json_encode
echo json_encode('HELLO "me"');
The output would be
"HELLO \"me\""
This is the internal representation of the string, within the Json object.
In the same way, using Qt, how can I retrieve the internal representation of a string, as it would be encoded as a value, within a Json formatted string?
It's really not that difficult. Start by building up the structure with QJsonObjects
QJsonObject obj;
obj.insert("tag1", QString("Some text"));
Then use QDocument to get a string in Json format
QJsonDocument doc(obj);
QByteArray data = doc.toJson(QJsonDocument::Compact);
QString jsonString(data);
This will produce a string, in the form of: -
{ "tag1" : "Some Text" }
Separate items into a list, splitting on ':'
QStringList items = jsonString.split(':', QString::SkipEmptyParts);
There should be 2 items in the list, the second being the value section of the Json string
"Some Test"}
Remove the final '}'
QString value = items[1].remove('}');
Of-course, you will need to do error checking and be aware that if you have a ':' or '}' in the original string, then you'll need to check for them first.
Original answer doesn't handle : and } inside of string correctly. A similar approach using array which requires only stripping []:
QString encodeJsonStringLiteral(const QString &value)
{
return QString(
QJsonDocument(
QJsonArray() << value
).toJson(QJsonDocument::Compact)
).mid(1).chopped(1);
}
ab"c'd becomes "ab\"c'd"
Or, if you don't need double quotes around the string, replace with .mid(2).chopped(2)

trouble passing parameters to console application

I have trouble with correctly launching cloc 1.62 from windows command line using qprocess. Here is what i have:
A QStringList with all the languages cloc recognizes;
QStringList languages;
languages<<"\"ABAP\""<<"\"ActionScript\""<<"\"Ada\""<<"\"ADSO/IDSM\""<<"\"AMPLE\""<<"\"Ant\""<<"\"Apex Trigger\"";
Then i create a qstring consisting of all of the list's elements separated by comma, exept for one, which is stored in a lang variable;
QString langsinuse;
for (int i=0;i<languages.length();i++)
{
if (languages.at(i) != lang)
{
if (langsinuse.isEmpty())
{
langsinuse=langsinuse+languages.at(i);
}
else
{
langsinuse=langsinuse+','+languages.at(i);
}
}
}
then i build an arguments stringlist and launch the process:
QStringList arguments;
QProcess cloc;
QString params="cloc-1.62.exe --xml --by-file --report-file="+
list1.at(1).trimmed()+'/'+name+'/'+"report.xml"+" --exclude-lang="+langsinuse+" "+distr;
arguments<<"/k"<<params;
cloc.startDetached("cmd.exe",arguments,"cloc/");
But somehow the spaces in the languages names are not considered escaped and every word separated by space considered a different parameter for cloc, even though both words are in double quotes (for example "\"Apex Trigger\"") and clock produces a bunch of errors.
(50 errors:
Unable to read: Trigger","Arduino
Unable to read: Sketch","ASP","ASP.Net","Assembly","AutoHotkey","awk","Bourne)
It's the error that happens when you don't put a language's name that contains spaces in double quotes, int the --exclude-lang= option (for example --exclude-lang=Apex Trigger will cause an error, --exclude-lang="Apex Trigger" won't)
However if i just save the whole command i build in qt and save it in some batch file it runs just fine.
Am i missing something in escaping the double quotes correctly?
Ok i just had to pass arguments separately and not as a single string.
arguments<<"/k"<<"cloc-1.62.exe" <<"--xml"<<"--by-file"<<"--report-file="+
list1.at(1).trimmed()+'/'+name+'/'+"report.xml"<<"--exclude-lang="+langsinuse<<distr;

Qt, QUrl, QUrlQuery: Encoding special character in a query string

I create a URL query like this:
QString normalize(QString text)
{
text.replace("%", "%25");
text.replace("#", "%40");
text.replace("‘", "%27");
text.replace("&", "%26");
text.replace("“", "%22");
text.replace("’", "%27");
text.replace(",", "%2C");
text.replace(" ", "%20");
return text;
}
QString key = "usermail";
QString value = "aemail#gmail.com";
QUrlQuery qurlqr;
qurlqr.addQueryItem(normalize(key), normalize(value));
QString result = qurlqr.toString();
The result that's be expecting is :
usermail=aemail%40gmail.com.
But I received:
usermail=aemail#gmail.com
I don't know why. Can you help me?
(I'm using Qt5 on Win7)
QUrlQuery's toString by default decodes the percent encoding. If you want the encoded version try:
qurlqr.toString(QUrl::FullyEncoded)
Also you don't need to manually encode the string by replacing characters; you could instead use QUrl::toEncoded() (I suggest you read the QUrlQuery documentation).

Using XQUERY to retrieve attributes value

Is it possible to use XQUERY to retrieve the attributes filename from the following XML? I am trying to use /preFileDoc/inpXML/#filename but it doesn't work...
<?xml version="1.0"?>
<preFileDoc xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">
<senderId>ABC</senderId>
<receiverId>XYZ</receiverId>
<tranxCode>A001</tranxCode>
<inpXML version="1.0" encoding="UTF-8">
<soap-env:Envelope>
<soap-env:Header msgcode="SPPCONVAKT" orig-system="002FTB" refid="65355ff50a172064484bf9da64c1e245" timestamp="2009-02-11 21:00:10.741" filename="SPPCONVAKT20090128001.dat"/>
<soap-env:Body>
text1
text2
</soap-env:Body>
</soap-env:Envelope>
</inpXML>
</preFileDoc>
ps: Sometimes the filename attributes is sent as fileName in the incoming XML..thinking to retrieve value from attributes #filename OR #fileName.. can it achieve in single XQUERY? Thanks for advice...
I think your XPath is incomplete. The last child-step / in /preFileDoc/inpXML/#filename only matches attributes of the inpXML element, not its descendants.
One way to solve the problem would be the //-step:
/preFileDoc/inpXML//#filename
Note that this would find all attributes named filename in the soapenv:Body, too.
A more robust way would thus be to declare the soapenv prefix in the XQuery:
declare namespace soap-env="http://schemas.xmlsoap.org/soap/envelope/";
return /preFileDoc/inpXML//soap-env:Header/#filename
Finally, the different capitalizations of filename can be worked around by specifying both:
declare namespace soap-env="http://schemas.xmlsoap.org/soap/envelope/";
return /preFileDoc/inpXML//soap-env:Header/(#filename | #fileName)
You can take the union of multiple attributes. It will be unlikely that this attribute will appear multiple times with different casing, so that should always return a single node:
//soap-env:Header/#filename | //soap-env:Header/#fileName
Optionally, you could wrap it in parentheses, and add [1] behind it, to always take the first result.
(//soap-env:Header/#filename | //soap-env:Header/#fileName)[1]
If you replace the union with a comma, which creates a sequence instead of a document order node set, you can add a default as well at the end. Maybe not very usefull here, but perhaps in other situations:
(//soap-env:Header/#filename , //soap-env:Header/#fileName, "default.dat")[1]
HTH!
You need to respect and take into account the SOAP XML namespace!
Since I don't know what you're using, I cannot tell you how to do this - but there's the xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/" on the root node, and your #filename attribute is on the <soap-env:Header .... /> node - so you need to include the XML namespace in your XQuery.
In .NET / C#, you could do it like this (using the "older" XmlDocument style which supports XPath directly):
// define test XML
string xmlContent =
#"<?xml version='1.0'?>
<preFileDoc xmlns:soap-env='http://schemas.xmlsoap.org/soap/envelope/'>
<senderId>ABC</senderId>
<receiverId>XYZ</receiverId>
<tranxCode>A001</tranxCode>
<inpXML version='1.0' encoding='UTF-8'>
<soap-env:Envelope>
<soap-env:Header msgcode='SPPCONVAKT' orig-system='002FTB' refid='65355ff50a172064484bf9da64c1e245' timestamp='2009-02-11 21:00:10.741' filename='SPPCONVAKT20090128001.dat'/>
<soap-env:Body>
text1
text2
</soap-env:Body>
</soap-env:Envelope>
</inpXML>
</preFileDoc>";
// create XmlDocument and load test data
XmlDocument doc = new XmlDocument();
doc.LoadXml(xmlContent);
// define XML namespace manager and add the SOAP namespace to it
XmlNamespaceManager mgr = new XmlNamespaceManager(doc.NameTable);
mgr.AddNamespace("soap", "http://schemas.xmlsoap.org/soap/envelope/");
// use XPath and the XML namespaces to grab the <Header> node
// the first two nodes <preFileDoc> and <inpXML> are not inside any explicit
// XML namespace
// but the next two (<Envelope> and <Header>) are in the "soap" XML namespace
XmlNode header = doc.SelectSingleNode("/preFileDoc/inpXML/soap:Envelope/soap:Header", mgr);
// read the "filename" attribute from the header node
if(header != null && header.Attributes["filename"] != null)
{
string fileName = header.Attributes["filename"].Value;
}

How do I find out which suffix user has chosen when using a QFileDialog?

Well I'm using the following code to get the filename for a file that needs to be stored ..
QString fileName = QFileDialog::getSaveFileName(this, tr("Save File"),"/home/user/MyDocs/",tr("JPG files (*.jpg);;BMP files (*.bmp);;PNG files (*.png)"));
I'm providing the user with a number of options regarding the file format in which the file is to be saved. However, the returned QString only gives me the prefix filename the user have chosen, not the suffix and thus I don't know which file format the user chose. How can I detect such a file format?
The code in the question works in Windows (Qt 4.6.2 and Win XP). fileName contains the selected extension. But you are obviously using something else Windows, so you could try this workaround:
QFileDialog dialog(this, tr("Save as ..."), "/home/user/MyDocs/");
dialog.setAcceptMode(QFileDialog::AcceptSave);
QStringList filters;
filters << "JPG files (*.jpg)" << "BMP files (*.bmp)" << "PNG files (*.png)";
dialog.setNameFilters(filters);
if (dialog.exec() == QDialog::Accepted)
{
QString selectedFilter = dialog.selectedNameFilter();
QString fileName = dialog.selectedFiles()[0];
}
That is a slighty modified code from here.
You need to use the 5th optional string
I usually do it like this:
#define JPEG_FILES "JPG files (*.jpg)"
#define BMP_FILES "BMP files (*.bmp)"
#define PNG_FILES "PNG files (*.png)"
QString selectedFilter;
QString fileName = QFileDialog::getSaveFileName(this, tr("Save File"),
"/home/user/MyDocs/",
JPEG_FILES ";;" BMP_FILES ";;" PNG_FILES, &selectedFilter);
if (fileName.isNull())
return;
if (selectedFilter == JPEG_FILES) {
...
} else if (selectedFilter == BMP_FILES) {
...
} else if (selectedFilter == PNG_FILES) {
...
} else {
// something strange happened
}
The compiler takes care to concatenate the literal strings in the argument.
I'm not sure how the returned string interacts with tr(). You'll have to test and find out. probably need to un-translate it.
It could have been nicer if the function would return the index of the selected filter but alas, it does not.
A nicer solution would be to put the filters in a list, create a string from it and then compare to the returned selected filter string to the ones in the list. This would also solve the tr() problem.
Have a look to this discussion. It uses QFileInfo on the string that was entered in a QFileDialog.

Resources