简体   繁体   中英

Reading a XML file in C++ with TinyXML2

I'm pretty new to using XML in C++ and i'm trying to parse a list of files to download. THe XML file I'm using is generated via PHP and looks like this:

<?xml version="1.0"?>
<FileList>
  <File Name="xxx" Path="xxx" MD5="xxx" SHA1="xxx"/>
</FileList>

The code I'm using in C++ is the following, which I came up using some online tutorials (it's included in some global function):

tinyxml2::XMLDocument doc;

doc.LoadFile("file_listing.xml");
tinyxml2::XMLNode* pRoot = doc.FirstChild();
tinyxml2::XMLElement* pElement = pRoot->FirstChildElement("FileList");
if (pRoot == nullptr)
{
    QString text = QString::fromLocal8Bit("Error text in french");
    //other stuff
}
else
{
    tinyxml2::XMLElement* pListElement = pElement->FirstChildElement("File");
    while (pListElement != nullptr)
    {
        QString pathAttr = QString::fromStdString(pListElement->Attribute("Path"));
        QString md5Attr = QString:: fromStdString(pListElement->Attribute("MD5"));
        QString sha1Attr = QString::fromStdString(pListElement->Attribute("SHA1"));

        QString currentPath = pathAttr.remove("path");
        QString currentMd5 = this->fileChecksum(currentPath, QCryptographicHash::Md5);
        QString currentSha1 = this->fileChecksum(currentPath, QCryptographicHash::Sha1);

        QFile currentFile(currentPath);

        if (md5Attr != currentMd5 || sha1Attr != currentSha1 || !currentFile.exists())
        {
            QString url = "url" + currentPath;
            this->downloadFile(url);
        }

        pListElement = pListElement->NextSiblingElement("File");
    }

Problem is, I get an error like "Access violation, this was nullptr" on the following line:

tinyxml2::XMLElement* pListElement = pElement->FirstChildElement("File");

Since I'm far from a pro when it comes to coding and I already searched the internet up and down, I hope that someone here can provide me some pointers.

Have a good day, folks.

According to the reference for tinyxml2::XMLNode FirstChild():

Get the first child node, or null if none exists.

This line will therefore get the root node:

tinyxml2::XMLNode* pRoot = doc.FirstChild();

Meaning when you attempt to find a FileList node within the root node it returns null.

To avoid the access violation, check your pointers are valid before using them. There is an if check for pRoot but the line immediately before it tries to call a function on pRoot. There is no if check for pElement so this is why you get an access violation. As well as checking pointers are valid, consider adding else blocks with logging to say what went wrong (eg "could not find element X"). This will help you in the long run - XML parsing is a pain, even with a library like Tinyxml, there are always teething problems like this, so getting into the habit of checki g pointers and logging out helpful messages will definitely pay off.

I don't know if you have C++17 available, but you can remove a lot of noise by using auto* and if-init-expressions (or rely on the fact that pointers can be implicitly converted to boolean values.)

The main issue with your code is you were not using XMLElement* but instead a XMLNode . The function tinyxml2::XMLDocument::RootElement() automatically gets the top-most element for you.

Because you have an xml declaration at the top, FirstChild returns that...which doesn't have any children, so the rest of the code fails.

By using RootElement tinyxml knows to skip any leading non-element nodes (comments, doctypes, etc.) and give you <FileList> instead.


    tinyxml2::XMLDocument doc;
    auto err = doc.LoadFile("file_listing.xml");
    if(err != tinyxml2::XML_SUCCESS) {
        //Could not load file. Handle appropriately.
    } else {
        if(auto* pRoot = doc.RootElement(); pRoot == nullptr) {
            QString text = QString::fromLocal8Bit("Error text in french");
            //other stuff
        } else {
            for(auto* pListElement = pRoot->FirstChildElement("File");
                pListElement != nullptr;
                pListElement = pListElement->NextSiblingElement("File"))
            {
                QString pathAttr = QString::fromStdString(pListElement->Attribute("Path"));
                QString md5Attr = QString:: fromStdString(pListElement->Attribute("MD5"));
                QString sha1Attr = QString::fromStdString(pListElement->Attribute("SHA1"));

                QString currentPath = pathAttr.remove("path");
                QString currentMd5 = this->fileChecksum(currentPath, QCryptographicHash::Md5);
                QString currentSha1 = this->fileChecksum(currentPath, QCryptographicHash::Sha1);

                QFile currentFile(currentPath);
                if(md5Attr != currentMd5 || sha1Attr != currentSha1 || !currentFile.exists()) {
                    QString url = "url" + currentPath;
                    this->downloadFile(url);
                }
            }
        }
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM