Problem reading an Xml file with QXmlStreamReader



  • Ηi,
    The code that follows reads a file and if a patient already exists, shows an error message, otherwise it calls the function
    CreateXMLFile. The problem is that it gets in the while loop only once and the value of the id2 is always the same.
    What can i do?

    @void QXSRExample::ReadXMLFile(){
    flag=0;
    QFile* file = new QFile("c:/anna.xml");
    if (!file->open(QIODevice::ReadWrite | QIODevice::Text)) {
    QMessageBox::critical(this,
    "QXSRExample::ReadXMLFile",
    "Couldn't open anna.xml",
    QMessageBox::Ok);
    return;
    }
    QXmlStreamReader xml(file);
    while(!xml.atEnd() ) {
    if(xml.name() == "patient_id") {
    id2=xml.readElementText();
    if (itemId==id2)
    flag=1;}
    xml.readNext();
    }
    xml.clear();
    if (flag==1)
    QMessageBox::warning(this,"Error","O asthenis einai idi kataxwrimenos",QMessageBox::Ok);
    else
    CreateXMLFile();
    file->close();
    }

    void QXSRExample::CreateXMLFile(){
    //Anoigma tou arxeiou
    QString filename = ("c:/anna.xml");
    QFile file2(filename);
    if (!file2.open(QIODevice::Append | QIODevice::Text)){
    //emfanisi sxetikou minimatos gia sfalma
    QMessageBox::warning(0, "Write only", "The file is in write only mode");}
    QXmlStreamWriter* xmlWriter = new QXmlStreamWriter();
    xmlWriter->setDevice(&file2);
    xmlWriter->writeStartElement("examinations");
    xmlWriter->writeStartElement("patient_exams");
    xmlWriter->writeStartElement("patient_id");
    xmlWriter->writeCharacters (itemId);
    xmlWriter->writeEndElement();
    ……………etc
    @



  • Please, reformat your code to use proper, consistent indenting. That will make it much easier to spot the issue you are running into.

    I really don't get the logic of starting to write an XML file while trying to read that same file. How is that supposed to work?



  • first i read the file to see if there is a patient with the same id. if there isn't a patient with that id i write to the file the examinations for that patient.

    i don't understand how to make my code



  • I mean a formatting like below, makes your code more readable and thus makes it easier to spot what is going wrong.

    @
    void QXSRExample::ReadXMLFile(){
    flag=0;
    QFile* file = new QFile("c:/anna.xml");
    if (!file->open(QIODevice::ReadWrite | QIODevice::Text)) {
    QMessageBox::critical(this,
    "QXSRExample::ReadXMLFile",
    "Couldn't open anna.xml",
    QMessageBox::Ok);
    return;
    }
    QXmlStreamReader xml(file);
    while(!xml.atEnd() ) {
    if(xml.name() == "patient_id") {
    id2=xml.readElementText();
    if (itemId==id2)
    flag=1;
    }
    xml.readNext();
    }
    xml.clear();
    if (flag==1)
    QMessageBox::warning(this,"Error","O asthenis einai idi kataxwrimenos",QMessageBox::Ok);
    else
    CreateXMLFile();
    file->close();
    }
    @

    I don't think it is a good idea to try to write to a file from the function that is supposed to read from it. IMHO, it is better to separate that into different functions, and don't call the one from the other.



  • In that way i had done the format of the code.

    ok, i will seperate the two functions but you didnt response to my problem.

    The problem is that it gets in the while loop only once and the value of the id2 is always the same.
    it doesnt read the next patient_id



  • [quote author="annatz" date="1314113534"]In that way i had done the format of the code.[/quote]
    But that is not what you posted, so not what we could see and study.
    [quote]
    ok, i will seperate the two functions but you didnt response to my problem.

    The problem is that it gets in the while loop only once and the value of the id2 is always the same.
    it doesnt read the next patient_id
    [/quote]

    Could you post the XML that you are trying to read then? We can not check if the code is at fault in this case.



  • i write the xml but at the preview it is different.

    @
    <examinations>
    <patient_exams>
    <patient_id>1</patient_id>
    <lab_test>
    <lab_id>Mikro</lab_id>
    <test_id>UREA</test_id>
    <specimen>blood</specimen>
    </lab_test>
    </patient_exams>
    </examinations>
    @

    the number 1 that is after examinations is after patient_id. and after examinations is the tag patient_exams

    Edit: reformatted XML to make it more readable. Please do that yourself next time; Andre



  • With the above piece of XML, and the code that you showed, you will encounter the patient_id tag twice: once for the openening, and once for the closing tag. id2 will contain the text "1" on the first call, and be empty for the second (read the docs on readElementText for an explanation why).

    It is obviously always the same, because the XML does not change all of a sudden during the reading (that is: you should not change the XML, as I told you before).



  • Have you considered using "QDomDocument":http://doc.qt.nokia.com/latest/qdomdocument.html instead?

    This will create an in-memory representation of your XML document which allows for searching and inserting / removing nodes.



  • Yes, but QDomDocument is considdered EOL and is thus not wise to use in new code.



  • I change my code like this:

    @void QXSRExample::ReadXMLFile(){
    flag=0;
    QFile* file = new QFile("c:/anna.xml");
    if (!file->open(QIODevice::ReadWrite | QIODevice::Text)) {
    QMessageBox::critical(this,
    "QXSRExample::ReadXMLFile",
    "Couldn't open anna.xml",
    QMessageBox::Ok);
    return;
    }
    QXmlStreamReader xml(file);
    while(!xml.atEnd() && !xml.hasError()){
    QXmlStreamReader::TokenType token = xml.readNext();
    if(token == QXmlStreamReader::StartDocument)
    continue;
    while(!(xml.tokenType() == QXmlStreamReader::EndElement && xml.name() == "patient_id")) {
    if(xml.tokenType() == QXmlStreamReader::StartElement) {
    if(xml.name() == "patient_id") {
    id2=xml.readElementText();
    if (itemId==id2)
    flag=1;
    }
    }
    xml.readNextStartElement();
    }
    }
    xml.clear();
    if (flag==1)
    QMessageBox::warning(this,"Error","O asthenis einai idi kataxwrimenos",QMessageBox::Ok);
    else
    CreateXMLFile();
    file->close();
    }@

    but now the program craches.



  • Again: please make your code easy to read here. Take care of indentation and the likes.

    Then, your code looks like nonsense. First you iterate through the whole XML in search of a StartDocument tag, and then you do nothing with it. Then, you expect there is more to read?
    A crash is easy to debug usually. Your debugger will tell what line triggered the crash.



  • [quote author="Andre" date="1314164753"]Yes, but QDomDocument is considdered EOL and is thus not wise to use in new code. [/quote]

    It is? It is not listed as deprecated in the documentation and the "module maturiy list":http://labs.qt.nokia.com/2011/05/12/qt-modules-maturity-level-the-list/ also states it as done (not deprecated). Is there another source to look at when evaluating module lifetime?



  • XML DOM should never be EOL. It is the only means to manipulate an XML document in-memory. And sometimes there are cases where you just plain cannot use a stream writer, e.g. when you need to add nodes at two different places in the tree simultaneously.



  • Let me put it this way: if I am wrong, then Thiago was wrong. He's the one saying something along these lines, but I can not remember if it was on IRC or on the mailinglist.



  • Maybe it's EOL in the sense of "no new features". But there's far too much code out there that relies on QDom. One should consider the stream classes for new projects, though.



  • EOL as in: no new features, no real maintenance (only grave bugs might get fixed), and no longer the recommended way to do things. Of course, not as: will be removed. That's how I understood it, at least.



  • I changed my code.
    It doesnt matter that the program will find twice the tag patient_id.
    The problem is that in the xml file are the same tags (from examinations to /examinations) many times, and i tried to debug my code and i saw that my code reads the xml (from examinatios to /examinations) only once. it "thinks" that it is the end of the file but it isnt ( it has more tags).

    @
    while(!xml.atEnd() && !xml.hasError()){
    if (xml.name() == "patient_id") {
    id2=xml.readElementText();
    if (itemId==id2)
    flag=1;
    }
    xml.readNext();
    }
    @



  • Hi,

    I have created some checkboxes with some examinations.

    @checkbox1 = new QCheckBox("Geniki Aimatos", this);
    checkbox2 = new QCheckBox("UREA", this);
    checkbox3 = new QCheckBox("CREA", this);@

    but i want to change my code and read the examinations from an xml file. the xml may change in the future.It may be bigger.
    So how can i create the checkboxes? each checkbox should have a different name. if i count the examinations of the file, how can i define the checkboxes?

    2 replies
    November 7, 2011
    Lukas Geyer Lukas Geyer
    Gene Splicer
    736 posts

    Groupie L1
    
     
    |report
    

    Use QXmlStreamReader to read the XML, create QCheckBoxes on the fly and add them to a layout, for example a QVBoxLayout to have them aligned properly. Use setObjectName() to associate a unique identifier with each checkbox.


    I did what you said (Lucas), but can you explain me how to use the setObjectName() please?
    i can't find any documentation.

    my code is :
    QCheckBox* checkbox1=new QCheckBox();
    @File* file = new QFile("c:/exetaseis.xml");
    if (!file->open(QIODevice::ReadOnly | QIODevice::Text)) {
    QMessageBox::critical(this,
    "QXSRExample::ok",
    "Couldn't open exetaseis.xml",
    QMessageBox::Ok);
    return;
    }

    QXmlStreamReader xml(file);
    while (!xml.atEnd() && !xml.hasError()) {
    if(xml.tokenType() == QXmlStreamReader::StartElement)
    if (xml.name() == "lab_test"){
    arithmos_exetasewn++;
    checkbox1->setObjectName("check");
    }
    }

    xml.readNext();

    _layout2->addWidget(checkbox1);
    @

    Is the way I used the setobjectname correct? and then i should use sth like this
    @QList<QCheckBox *> allCheckboxes=checkbox1->findChildren<QCheckBox *>();
    @



  • [quote author="annatz" date="1320921838"]
    I did what you said (Lucas), but can you explain me how to use the setObjectName() please?
    i can't find any documentation.
    [/quote]

    Then you did not search properly. Here "it is":http://doc.qt.nokia.com/4.7/qobject.html#objectName-prop

    Just as a side note: Use Qt Assistant (it's installed with every Qt) for searching the docs. And you did spot the DOC tab here on DevNet already, didn't you? It has a search function too.



  • You most probably want to set an unique name for your objects; checkbox1->setObjectName(QString("check") + arithmos_exetasewn) instead of checkbox1->setObjectName("check"). As every QObject can be seen as an unique entity (one reason why there is no copy ctor for QObject) there shouldn't be two QObjects having the same objectName (at least not in the same "relationship tree").

    findChild() and findChildren() returns children of the invoking object. checkbox1 does not have any children. To find all checkboxes use layout2->findChildren<QCheckBox*>(), to find a specific checkbox use layout2->findChild<QCheckBox*>("checkboxN") where N is the number of checkbox you are looking for.

    In addition, xml.readNext() is outside of your while - you will end up in a infinite loop.



  • and how can i use for example the second checkbox?
    i used @_layout2->findChild<QCheckBox*>("check2");@ and then how can i add a value to that checkbox?



  • I am trying to understand your requirements - again.
    You want to create a series of checkboxes based on the contents of an XML file (or of a part of such a file, not very clear), and I will assume you will want to write back the settings afterwards. Right?

    I think you were already told in the past more than once, that you should create an in-memory representation of your data tree, as you can not read and write back small parts of it. You will need to write the whole file in one go, and thus you need all information that needs to go into the file in memory.

    So, if you have a decent representation of your data in memory ("data store" from now on), only then the question arrises how to build up a UI to represent that data. So, you get:
    (XML) storage format ↔ Data store ↔ UI, in stead of
    XML ↔ UI.

    That is a big difference. Try to focus on one aspect at a time. First come up with a good, usable implementation of a data store that really is able to represent all the data you need to handle in a sane way, and has a good API for the rest of your application to use. Then, implement the loading of data (XML or whatever format you fancy) from a storage medium into your application, and the saving of the data back into that storage. You might even considder using ready-to-use frameworks for that, like QtORM or Boost::serialization. The representation and manipulation of your data comes on top of that data store. It is a really basic software design pattern to think of your application as layers. A common way to separate your application into layers is Data ↔ Business ↔ UI.

    Then, on your UI design issue:
    If you have a list of examinations that be any length (from 0 to hundreds) that you need to present, I would not fiddle around with creating and destroying QCheckBox objects at runtime and trying to find reference to them back using the object name. That is not very strong design at all. I would use the model-view framework instead. Items in such a model can be checkable easily. So, I would create a model (QAbstractListModel-decendent?) to show a view on this aspect of your data store. The model can then just modify the data store if a checkbox is checked or unchecked in the list.

    Try not to take this personally, but it seems to me that you are trying to create a medical application, dealing with patient data. Is that correct? These applications are important, can can mean the difference between life and death and much suffering if they cause medial mistakes. It seems to me that you are not qualified to build such applications. It is one thing to screw up the data store of a small store or a personal addressbook or something like that, it is quite another to screw up medical records.



  • my program reads an xml file with the patients' examinations. i want to create as many checkboxes as the examinations. But the xml file might change in the future and the number of the examinations might change.

    You are right, i am trying to create a medical application. it is a project for my univercity and i am trying on my own and i find it much difficult.



  • I would suggest you go to your teacher for help then. Your problem seems not be (mainly) with Qt, but with software design in general. That is a topic tought at the university, not at the Qt forums.



  • [quote author="annatz" date="1321086833"]and how can i use for example the second checkbox?
    i used @_layout2->findChild<QCheckBox*>("check2");@ and then how can i add a value to that checkbox?[/quote]

    What does "add value" mean? You can set the checkboxes value (checked, not checked, intermediate) using QCheckBox::setCheckState(). If you want to attach some data to it (which can be retrieved later) you can use QObject::setProperty() (every widget is a QObject).

    However, although it is possible to use the UI as "data store" it is - as Andre already stated - usually way better to use a distinct "data store" layer.



  • my problem wasn't how to store my data at a data store. my application will show on the screen some checkboxes with my data. how can i dynamically create those checkboxes? i did it like

    @checkbox1 = new QCheckBox("Geniki Aimatos", this);
    checkbox2 = new QCheckBox("UREA", this);
    checkbox3 = new QCheckBox("CREA", this);
    checkbox4 = new QCheckBox("LDH", this);
    checkbox5 = new QCheckBox("TBIL", this);@

    but i dont know how to create "x" checkboxes and add them a label like "Geniki Aimatos".



  • So... if you have your data in a data store (that is no problem, right?), why don't you simply put a QAbstractListModel-derived model on top of that data store, and make the items checkable?



  • [quote author="annatz" date="1321123713"]my problem wasn't how to store my data at a data store. my application will show on the screen some checkboxes with my data. how can i dynamically create those checkboxes? i did it like

    @checkbox1 = new QCheckBox("Geniki Aimatos", this);
    checkbox2 = new QCheckBox("UREA", this);
    checkbox3 = new QCheckBox("CREA", this);
    checkbox4 = new QCheckBox("LDH", this);
    checkbox5 = new QCheckBox("TBIL", this);@

    but i dont know how to create "x" checkboxes and add them a label like "Geniki Aimatos".[/quote]

    Loop over the data that you have extracted from the XML data. You should have put that into some decent container (QList, QMap...) beforehand.

    To ease your life, you could go with a [[Doc::QStandardItemModel]] or use a [[Doc:QTreeWidget]]. That saves you the task of writing your own AQIM based item model. The latter would be - don't take it personal - a too complicated task for you.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.