How to correctly access xml attributes using pugixml? - c++

I'm new to xml, and using the pugixml documentation I haven't been able to read a node attribute to the console. The core of the issue is that I don't understand how the node accessing structure works and I haven't been able to figure it out using my code.
The code provided is a lightly modified line from the actual pugixml manual, so I'm not sure why it isn't working.
XML FILE LOADED
<?xml version="1.0" encoding="utf-8"?>
<settings description="settings root description">
<!--settings for the options menu-->
<options description="game options">
<resolution description="resolution in 16:9 ratio">6</resolution>
<volume description="volume setting">0</volume>
</options>
</settings>
C++ CODE TRYING TO USE XML FILE
//set up and load settings xml doc
pugi::xml_document settingsXML;
pugi::xml_parse_result settingsResult = settingsXML.load_file("SFMLVania/include/Settings.xml");
std::cout << "Root description: " << settingsXML.root().attribute("description").as_string();
I'm expecting to see:
"Root description: settings root description"
in the console.
Instead, I'm getting:
"Root description: "
SECOND ATTEMPT -- to try and just get any data and find out where I am in the tree:
std::cout << "Second attempt: " << settingsXML.first_attribute().as_string();
All I got from the second attempt was the console spitting out: "Second attempt: "

It turns out, my load path didn't exist, I had forgotten to use ../ to go up a directory and out of the Debug folder where the .exe was stored. I figured this out by testing the load result that could be passed as a bool.
if (settingsResult)
{
std::cout << "returned true\n";
}
else
std::cout << "returned false\n";
if (!settingsResult)
{
std::cout << "also returned false\n";
}
I included some redundant returns in case the bool value didn't function as expected.
The result showed that there was no file loaded (it returned false), and when I reexamined my file structure and added the ../ as a test to the file path, it returned true.

For anyone that comes across this, you can prevent this error by adding a minimal parse check after the assigning of settingsResult like this:
pugi::xml_document settingsXML;
pugi::xml_parse_result settingsResult = settingsXML.load_file("SFMLVania/include/Settings.xml");
// Catch parsing error
if (!settingsResult )
{
std::cout << "ERROR: " << settingsResult.description() << '\n';
return -1;
}
This will catch a bad parse and log the error to the console.

Related

Running arbitrary SQL commands MySQL C++ (X DevAPI)?

I've connected my C++ project to MySQL and successfully created a session. I was able to create a Schema. My issue is that when I try to run simple arbitrary queries like USE testSchema SHOW tables; using the MySQL/C++ api, I run into SQL syntax errors. When I run the function directly in the MySQL shell, the query runs perfectly fine.
Here is the full code
const char* url = (argc > 1 ? argv[1] : "mysqlx://pct#127.0.0.1");
cout << "Creating session on " << url << " ..." << endl;
Session sess(url);
{
cout << "Connected!" << endl;
// Create the Schema "testSchema"; This code creates a schema without issue
cout << "Creating Schema..." << endl;
sess.dropSchema("testSchema");
Schema mySchema = sess.createSchema("testSchema");
cout << "Schema Created!" << endl;
// Create the Table "testTable"; This code runs like normal, but the schema doesn't show
cout << "Creating Table with..." << endl;
SqlStatement sqlcomm = sess.sql("USE testSchema SHOW tables;");
sqlcomm.execute();
}
Here is the console output:
Creating session on mysqlx://pct#127.0.0.1 ...
Connected!
Creating Schema...
Schema Created!
Creating Table with...
MYSQL ERROR: CDK Error: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'SHOW tables' at line 1
The error You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'SHOW tables' at line 1 is a MySQL error that means I have a syntax error in the query, but when I take a closer look at a query, I see there is nothing wrong with it.
I've copied and pasted the code directly from the cpp file into the mysql shell and it runs perfectly. This tells me that something is up with the formatting of how I'm entering the query in the sql() function. But the documentation for the sql() function is really terse and.
Here is the reference to the sql() function: https://dev.mysql.com/doc/dev/connector-cpp/8.0/class_session.html#a2e625b5223acd2a3cbc5c02d653a1426
Can someone please give me some insight on where I'm going wrong? Also here here is the full cpp code for more context:https://pastebin.com/3kQY8THC
Windows 10
Visual Studio 2019
MySQL 8.0 with Connect/C++ X DevAPI
You can do it in two steps:
sess.sql("USE testSchema").execute();
SqlStatement sqlcomm = sess.sql("SHOW tables");
SqlResult res = sqlcomm.execute();
for(auto row : res)
{
std::cout << row.get(0).get<std::string>() << std::endl;
}
Also, you can use the Schema::getTables():
for(auto table : mySchema.getTables())
{
std::cout << table.getName() << std::endl;
}
Keep in mind that the Schema::getTables() doesn't show the Collections created by Schema::createCollection(). There is also a Schema::getCollections():
for(auto collection : mySchema.getCollections())
{
std::cout << collection.getName() << std::endl;
}

findAndGetString() in DCMTK returns null for the tag

I am developing a quick DICOM viewer using DCMTK library and I am following the example provided in this link.
The buffer from the API always returns null for any tag ID, eg: DCM_PatientName.
But the findAndGetOFString() API works fine but returns only the first character of the tag in ASCII, is this how this API should work?
Can someone let me know why the buffer is empty the former API?
Also the DicomImage API also the same issue.
Snippet 1:
DcmFileFormat fileformat;
OFCondition status = fileformat.loadFile(test_data_file_path.toStdString().c_str());
if (status.good())
{
OFString patientName;
char* name;
if (fileformat.getDataset()->findAndGetOFString(DCM_PatientName, patientName).good())
{
name = new char[patientName.length()];
strcpy(name, patientName.c_str());
}
else
{
qDebug() << "Error: cannot access Patient's Name!";
}
}
else
{
qDebug() << "Error: cannot read DICOM file (" << status.text() << ")";
}
In the above snippet name has the ASCII value "50" and the actual name is "PATIENT".
Snippet 2:
DcmFileFormat file_format;
OFCondition status = file_format.loadFile(test_data_file_path.toStdString().c_str());
std::shared_ptr<DcmDataset> dataset(file_format.getDataset());
qDebug() << "\nInformation extracted from DICOM file: \n";
const char* buffer = nullptr;
DcmTagKey key = DCM_PatientName;
dataset->findAndGetString(key,buffer);
std::string tag_value = buffer;
qDebug() << "Patient name: " << tag_value.c_str();
In the above snippet, the buffer is null. It doesn't read the name.
NOTE:
This is only a sample. I am just playing around the APIs for learning
purpose.
The following sample method reads the patient name from a DcmDataset object:
std::string getPatientName(DcmDataset& dataset)
{
// Get the tag's value in ofstring
OFString ofstring;
OFCondition condition = dataset.findAndGetOFString(DCM_PatientName, ofstring);
if(condition.good())
{
// Tag found. Put it in a std::string and return it
return std::string(ofstring.c_str());
}
// Tag not found
return ""; // or throw if you need the tag
}
I have tried your code with your datasets. I just replaced the output to QT console classes to std::cout. It works for me - i.e. it prints the correct patient name (e.g. "PATIENT2" for scan2.dcm). Everything seems correct, except for the fact that you apparently want to transfer the ownership for the dataset to a smart pointer.
To obtain the ownership for the DcmDataset from the DcmFileFormat, you must call getAndRemoveDataset() instead of getDataset(). However, I do not think that your issue is related that. You may want to try my modified snippet:
DcmFileFormat file_format;
OFCondition status = file_format.loadFile("d:\\temp\\StackOverflow\\scan2.dcm");
std::shared_ptr<DcmDataset> dataset(file_format.getAndRemoveDataset());
std::cout << "\nInformation extracted from DICOM file: \n";
const char* buffer = nullptr;
DcmTagKey key = DCM_PatientName;
dataset->findAndGetString(key, buffer);
std::string tag_value = buffer;
std::cout << "Patient name: " << tag_value.c_str();
It probably helps you to know that your code and the dcmtk methods you use are correct, but that does not solve your problem. Another thing I would recommend is to verify the result returned by file_format.loadFile(). Maybe there is a surprise in there.
Not sure if I can help you more, but my next step would be to verify your build environment, e.g. the options that you use for building dcmtk. Are you using CMake to build dcmtk?

QT: QXmlStreamReader always returns "Premature End of Document" error

I have strange issue with Qt QXmlStreamReader. I'am trying to parse simple document (note: it is generated using QXmlStreamWriter):
<?xml version="1.0" encoding="UTF-8"?>
<tex>
<used_by/>
<facade>
<tags>
<town_related></town_related>
<zone_related></zone_related>
<visual_related></visual_related>
<kind_related></kind_related>
<other>flamingo</other>
</tags>
<additional_textures>
<id>flamingo_top.psd</id>
</additional_textures>
</facade>
</tex>
Using this code:
QFile file(filename);
if (file.open(QFile::ReadOnly | QFile::Text))
{
QXmlStreamReader xmlReader(&file);
while (xmlReader.readNextStartElement())
{
/* same issue when uncommented:
if (xmlReader.name() == "tex")
t->readXml(xmlReader);//parse texture
else*/
xmlReader.skipCurrentElement();
}
if (xmlReader.hasError())
emit reportError(xmlReader.errorString());
}
...
And it always reports error "Premature end of document". Why? When debbuging, it seems, to all elements are parsed or skipped correctly.
I verified the behavior of your code. Indeed, it seems that readNextStartElement() does not correctly check for end of document. It only checks for start/end element to return its value, so if reading past the end of document, its internal call to readNext raises "premature end".
For a quick fix try checking for end of document yourself using readNext(), eg.:
while (!xml.atEnd()) {
if (xml.readNext() != QXmlStreamReader::EndDocument) {
if (xml.isStartElement())
std::cout << qPrintable(xml.name().toString()) << std::endl;
}
}
if (xml.hasError())
std::cout << (xml.errorString().toUtf8().constData()) << std::endl;

Why Isn't find_one working in MongoDB C++?

I have a MongoDB 3.0.7 database, created with the mongo shell. The following works fine:
% mongo test
> vs = db.myCollection.findOne({"somefield.subfield": "somevalue"})
but when I do this in C++:
mongocxx::instance inst{};
mongocxx::client conn{};
auto db = conn["test"];
bsoncxx::stdx::optional< bsoncxx::document::value> docObj;
try {
docObj =
db["myCollection"]
.find_one(document{} <<
"somefield.subfield" << "someValue" <<
bsoncxx::builder::stream::finalize);
} catch (mongocxx::exception::operation e) {
std::cerr << "Retrieval failed (and exception thrown)";
}
if (docObj == bsoncxx::stdx::nullopt)
std::cerr << "Failed to find object";
I get "Failed to find object". What am I missing here?
Update: 11/23/2015, 10:00
I've installed the latest cxx driver (0.3.0), and made the following changes:
mongocxx::instance inst{};
mongocxx::client *connPtr;
bsoncxx::stdx::string_view connectionString("mongodb://localhost");
connPtr = new mongocxx::client(mongocxx::uri(connectionString));
auto db = connPtr->database("test");;
bsoncxx::stdx::optional< bsoncxx::document::value> docObj;
try {
docObj =
db["myCollection"]
.find_one(document{} <<
"somefield.subfield" << "someValue" <<
bsoncxx::builder::stream::finalize);
} catch (mongocxx::exception::operation e) {
std::cerr << "Retrieval failed (and exception thrown)";
}
if (docObj == bsoncxx::stdx::nullopt)
std::cerr << "Failed to find object";
I'm back to exactly the same thing. Calling db.list_collections(document{}) retrieves no results.
The bsoncxx library has two document types, views and values. A document::value contains the actual document data, and a document::view is just a reference to some underlying value. Values must outlive the views that use them.
There's a bug in the new c++11 driver with how document::values are passed around. This code produces a document::value :
document{} << "someField" << "someValue" << finalize;
The collection.find_one() method takes a document::view, and document::values convert implicitly to document::views. Unfortunately, this means if you dynamically build a document in your call to find_one(), as above, you can shoot yourself in the foot:
collection.find_one(document{} << "someField" << "someValue" << finalize);
finalize makes a temporary document::value, then find_one converts that to a document::view. The temporary value is dropped on the floor, leaving your view value-less, like a dangling pointer.
A workaround is to make your value in a separate call, and keep it around:
document::value doc = document{} << "someField" << "someValue" << finalize;
collection.find_one(doc.view());
I suspect this is what's causing your queries to fail; if not, it's something to make your code resilient to nonetheless!
You can track this ticket for the real fix for this problem.

QXmlStreamReader says that i'm at the end of an XML file while in the middle of it

Hello the problem is that i'm reading a XML file using QXmlStreamReader to read in Appointments for my Calendar but when I'm switching from AppointmentSolo to AppointmentRepeat atEnd() returns true for some reason.
This is the XML file
<?xml version="1.0" encoding="UTF-8"?>
<AppointmentSolo>
<Length>1</Length>
<AppointmentSolo0>
<Date>10 09 2011</Date>
<Begin>15:11</Begin>
<End>23:12</End>
<Title>Final test</Title>
<Description>Final countdown</Description>
<hasNotify>1</hasNotify>
<notify>17</notify>
</AppointmentSolo0>
</AppointmentSolo>
<AppointmentRepeat>
<Length>1</Length>
<AppointmentRepeat0>
<Date>08 01 2014</Date>
<Begin>20:08</Begin>
<End>23:09</End>
<Type>MONTHLY</Type>
<Exceptions>
<Length>1</Length>
<Exception0>08 09 2014</Exception0>
</Exceptions>
<Title>Repeat test</Title>
<Description>FooBar</Description>
<hasNotify>0</hasNotify>
<notify>0</notify>
</AppointmentRepeat0>
</AppointmentRepeat>
And here is the part of my code which reads it and where the problem occurs.
if(Rxml.isEndElement() && Rxml.name() == "AppointmentSolo")
{
qDebug() << Rxml.atEnd() << Rxml.name() << Rxml.hasError();
Rxml.readNext();
qDebug() << Rxml.atEnd() << Rxml.name() << Rxml.hasError();
qDebug() << Rxml.error();
while(!Rxml.atEnd() && !Rxml.isStartElement())//om aan begin tag te zijn
{
Rxml.readNext();
qDebug() << Rxml.atEnd() << Rxml.name() << Rxml.hasError();
}
}
This is what is outputted
false "AppointmentSolo" false
true "AppointmentRepeat" true
3
It seems this is a QXmlStreamReader::NotWellFormedError
The parser internally raised an error due to the read XML not being well-formed.
But why is my XML not well formed then?
EDIT: it seems there happens an error (i added the "<< Rxml.hasError()")
XML can have only one root element, so your XML is invalid.