Use QDirIterator to create an XML files



  • Hi,

    I want to create an xml file representing the contents of a folder. For example :

    Home
        |Test             //a file
        |Documents        //a folder
             |MyDoc1      //a file
        |Movies           //a folder
             |MyMovie1   //a file
        |Pictures         //a folder
             |MyPicture1 //a file
    

    will be represented by

    <root>
        <file>
            <name>Test</name>
        </file>
       <Documents>
           <file>
               <name>MyDoc1</name>
          </file>  
       </Documents>
       <Movies>
           <file>
               <name>MyMovie1</name>
          </file>  
       </Movies>
       <Pictures>
           <file>
               <name>MyPicture1</name>
          </file>  
       </Documents>
    </root>
    

    Obviously I don't know in advance the number of subfolders and files.

    Currently, here is my code :

    folder2Xml.h:

    #ifndef FOLDERMODEL_H
    #define FOLDERMODEL_H
    
    #include <QObject>
    #include <QDir>
    #include <QDirIterator>
    #include <QFile>
    #include <QDebug>
    #include <QXmlStreamWriter>
    
    class FolderModel : public QObject
    {
        Q_OBJECT
    
    
    public:
        explicit FolderModel(QObject *parent = 0);
    
    public slots:
        QStringList getXml(QString pathDir);
        void getContents(QString path);
    
    private:
        QString m_pathDir;
        QFile m_pathXmlFile;
        QXmlStreamWriter m_stream;
    
    };
    
    #endif // FOLDERMODEL_H
    

    folder2xml.cpp

    #include "foldermodel.h"
    
    FolderModel::FolderModel(QObject *parent) : QObject(parent)
    {
    
    }
    
    QStringList FolderModel::getXml(QString pathDir)
    {
        m_pathDir = pathDir;
    
    
        qDebug() << "Entre dans FolderModel::getContents(QString dirPath)";
        QDir dir(pathDir);
        dir.setSorting(QDir::Type);
        QStringList contentsFiles = dir.entryList(QDir::Files | QDir::NoDotAndDotDot);
    
        m_pathXmlFile.setFileName("path/to/myXml.xml");
        if (m_pathXmlFile.exists()) m_pathXmlFile.remove();
    
    
    
        if (m_pathXmlFile.open(QFile::Append)){
            m_stream.setDevice(&m_pathXmlFile);
            m_stream.setAutoFormatting(true);
            m_stream.writeStartDocument();
            m_stream.writeStartElement("root");
            m_stream.writeTextElement("version", "0.4");
    
    
            this->getContents(m_pathDir);
    
            m_stream.writeEndElement(); // <version>
            m_stream.writeEndDocument();
        }
    
        return contentsFiles;
    
    }
    
    void FolderModel::getContents(QString path)
    {
        QDirIterator iterator(path, QDir::Files | QDir::AllDirs | QDir::NoDotAndDotDot, QDirIterator::Subdirectories);
        while (iterator.hasNext()) {
            QFileInfo fileInfo = iterator.fileInfo();
    
            //qDebug() << fileInfo.absoluteFilePath() << fileInfo.absolutePath() << iterator.fileName();
    
            if(fileInfo.isFile()){
                m_stream.writeStartElement("file");
                m_stream.writeTextElement("name", fileInfo.fileName());
                m_stream.writeTextElement("size", QString::number(fileInfo.size()));
                m_stream.writeEndElement();
    
            }
            else{
                m_stream.writeStartElement(iterator.fileName());
                this->getContents(fileInfo.absoluteFilePath());
                m_stream.writeEndElement();
                //qDebug() << iterator.next();
            }
            iterator.next();
    
        }
    }
    

    But I have a problem with the result. My problem is with loops (while and if) in getContents. I can't correctly insert the files in the appropriate XML nodes.

    Can you help me ?

    Thank you for advance

    Charlie.



  • Hi! To read folder structures of arbitrary depth you need to use recursion. I made a small example but you need to add all the error checking.

    folderstuff.h

    #ifndef FOLDERSTUFF_H
    #define FOLDERSTUFF_H
    
    #include <QString>
    
    QString dirToXml(QString path);
    
    #endif // FOLDERSTUFF_H
    

    folderstuff.cpp

    #include "folderstuff.h"
    
    #include <QXmlStreamWriter>
    #include <QStringList>
    #include <QDir>
    
    void readDir(QString path, QXmlStreamWriter &sw)
    {
        const auto dirs = QDir(path).entryList(QDir::Dirs | QDir::NoDotAndDotDot);
        for (const auto i : dirs) {
            sw.writeStartElement("directory");
            sw.writeAttribute("name", i);
            readDir(QDir(path+"/"+i).absolutePath(), sw);
            sw.writeEndElement();
        }
    
        const auto files = QDir(path).entryList(QDir::Files | QDir::NoDotAndDotDot);
        for (const auto i : files) {
            sw.writeStartElement("file");
            sw.writeAttribute("name", i);
            sw.writeEndElement();
        }
    }
    
    QString dirToXml(QString path)
    {
        QString result;
        {
            QXmlStreamWriter sw(&result);
            sw.setAutoFormatting(true);
            sw.writeStartDocument();
            sw.writeStartElement("rootdirectory");
            sw.writeAttribute("path", path);
            readDir(path, sw);
            sw.writeEndDocument();
        }
        return result;
    }
    

    main.cpp

    #include <QGuiApplication>
    #include <QFile>
    
    #include "folderstuff.h"
    
    int main(int argc, char *argv[])
    {
        QGuiApplication app(argc, argv);
        {
            const QString path("/home/charlie/downloads");
            QFile file("/home/charlie/dirlist.xml");
            file.open(QIODevice::WriteOnly);
            {
                QTextStream ts(&file);
                ts << dirToXml(path); // windows linebreaks: .replace("\n", "\r\n");
            }
        }
        return 0;
    }
    


  • the only addition, replace const auto i with auto& i, it's (very marginally) faster

    P.S.
    constness of i is implied by constness of container

    EDIT:
    @Wieland is right, the gain is very limited, don't know why but this old article stuck in my head. but if you use the value version use const as it's not implied



  • @VRonin Ah yes, you're right about the constness. But I'm not sure about the reference because QString is already implicitly shared. Anyways, good hint in general :-)



  • It's super ...
    Thank you very much for your help.

    And the more I discover a new way to use the for loop (C ++ 11, I guess?).

    Thanks again.


Log in to reply
 

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