Question

I'm trying to get a list of the iTunes albums, by parsing the XML library (iTunes Music Library.xml in the iTunes directory).

#include <iostream>
#include <QtCore>
#include <QFile>
#include <QtXml>

using namespace std;

void parse(QDomNode n) {

    while(!n.isNull()) {

        // If the node has children
        if(n.hasChildNodes() && !n.isNull()) {

            // We get the children
            QDomNodeList nChildren = n.childNodes();

            // We print the current tag name
            //std::cout << "[~] Current tag : <" << qPrintable(n.toElement().tagName()) << ">" << std::endl;

            // And for each sub-tag of the current tag
            for(int i = 0; i < nChildren.count(); i++) {

                // We get the children node
                QDomNode nChild = nChildren.at(i);
                // And the tag value (we're looking for *Album* here)
                QString tagValue = nChild.toElement().text();

                // If the tag isn't null and contain *Album*
                if(!nChild.isNull() && tagValue == "Album") {
                    // The album name is in the next tag
                    QDomElement albumNode = nChild.nextSiblingElement();
                    std::cout << "[-] Album found -> " << qPrintable(albumNode.text()) << std::endl;
                }

                // And we parse the children node
                parse(nChild);
            }
        }

        n = n.nextSibling();
    }
}

int main() {

    QDomDocument doc("Lib");
    QFile file("/Users/wizardman/QtRFIDMusic/Lib.min.xml");
    if(!file.open(QIODevice::ReadOnly))
        return 1;
    if(!doc.setContent(&file)) {
        file.close();
        return 1;
    }
    file.close();

    // Root element
    QDomElement docElem = doc.documentElement();

    // <plist> -> <dict>
    QDomNode n = docElem.firstChild().firstChild();

    cout << endl << "Album list" << endl;
    cout << "------------------------------------" << endl;


    parse(n);

    return 0;
}

The iTunes' XML is not really standart XML, the name of the album is stored in the node next to the <key>Album</key> for each entry. Here is what it looks like. I intentionnaly renamed some nodes for debugging purpose (to see if I reach them in my output).

And here is my output :

Album list
------------------------------------
[-] Album found -> J Dilla - Legacy Vol.1
[-] Album found -> J Dilla - Legacy Vol.2
[-] Album found -> J Dilla - Legacy Vol.1
[-] Album found -> J Dilla - Legacy Vol.2
[-] Album found -> J Dilla - Legacy Vol.2
[-] Album found -> J Dilla - Legacy Vol.2

I can't see why the loop is reparsing the first nodes. Any ideas ?

Was it helpful?

Solution

After running your code under my debugger... it appears that you're iterating through the children too many times. Meaning, you recursively traverse through the entire tree (repeatedly) at <dict>, the inner <dict>, <dict_FOCUS> and also <dict_FOCUS2>.

For me, it was easier just to iterate (without recursion) through the nodes using QDomNode::firstChildElement(QString); I can't guarantee that this is bullet proof.. but it's a start! ;)

// Root element
QDomElement docElem = doc.documentElement();

// <plist> -> <dict>
QDomNode n = docElem.firstChildElement().firstChildElement("dict");

qDebug() << "Album list";
qDebug() << "------------------------------------";

QDomNodeList list = n.childNodes();
int count = list.count();

for(int i = 0; i < count; ++i)
{
  QDomElement node = list.at(i).toElement();
  if(node.tagName().startsWith("dict_FOCUS"))
  {
    node = node.firstChildElement();
    while(!node.isNull())
    {
      if(node.text() == "Album" && node.tagName() == "key")
      {
        node = node.nextSiblingElement();
        if(!node.isNull() && node.tagName() == "string")
        {
          qDebug() << "[-] Album found -> " << qPrintable(node.text());
        }
      }
      node = node.nextSiblingElement();
    }
  }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top