Чтение тегов XML внутри StartElement (QXmlStreamReader)

Итак, я пытаюсь прочитать XML-документ скромного размера. Он устроен так:

<project identifier="project1">
    <author>Joe Smith</author>
    <author2>Rick Jones</author2>
    <path>projects/internal/project2</path>
    <version>1.51</version>
</project>
<project identifier="project2">
     <author>Terry Chimes</author>
     <author>Janie Jones</author>
     <path>projects/external/project2</path>
     <version>19.77</version>
</project>

... и так далее, для нескольких сотен проектов.

Я использую QXmlStreamReader Qt5.10, который, возможно, был создан (или задокументирован) садистами.

Я могу найти каждый project, используя xmlReader.readNextStartElement, или читая тег за тегом, пока не найду один с внутренними атрибутами (только project теги имеют атрибуты в этом файле).

Но как только я читаю один из этих родительских элементов, QXmlStreamReader всасывает все теги вплоть до закрывающего тега </project>. Проблема в том, что мне нужно получить некоторые из этих данных, в данном случае то, что находится внутри тегов <path></path>.

С помощью xmlReader.readElementText(QXmlStreamReader::IncludeChildElements я могу получить все данные, которые были скопированы, но это всего лишь один большой дамп данных без тегов.

Кто-нибудь знает, как я могу «перемотать» и прочитать внутренние теги? Или помешать потоковому считывателю качнуться вперед и высосать все данные?


person Dito    schedule 23.04.2018    source источник


Ответы (1)


Наиболее вероятным объяснением является то, что вы делаете что-то не так, QXmlStreamReader не должны пропускать внутренние элементы при разборе документа. Вы не предоставили свой исходный код, поэтому невозможно сказать, что именно вы сделали не так.

Вот мой пример кода, который отлично работает на примере, очень похожем на ваш, с Qt 5.9.2 на macOS 10.13.2:

#include <QCoreApplication>
#include <QDebug>
#include <QXmlStreamReader>
#include <QFile>
#include <QHash>

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    if (argc != 2) {
        qWarning() << "Usage: " << argv[0] << " <file>";
        return 1;
    }

    QFile file(argv[1]);
    if (!file.open(QIODevice::ReadOnly)) {
        qWarning() << "Failed to open file " << argv[1] << " for reading";
        return 1;
    }

    QXmlStreamReader reader(&file);
    QString currentProjectId;
    QHash<QString,QString> pathByProjectId;
    while(!reader.atEnd())
    {
        reader.readNext();

        if (reader.isStartDocument()) {
            continue;
        }

        if (reader.isEndDocument()) {
            break;
        }

        if (reader.isStartElement())
        {
            QStringRef elementName = reader.name();
            if (elementName == "project") {
                QXmlStreamAttributes attrs = reader.attributes();
                currentProjectId = attrs.value("identifier").toString();
            }
            else if (elementName == "path") {
                pathByProjectId[currentProjectId] = reader.readElementText(QXmlStreamReader::IncludeChildElements);
            }

            continue;
        }
    }

    for(auto it = pathByProjectId.constBegin(),
        end = pathByProjectId.constEnd(); it != end; ++it)
    {
        qDebug() << "Path for project " << it.key() << ": " << it.value();
    }

    file.close();

    return 0;
}

Вот слегка измененный ваш пример, который я передаю в этот образец программы:

<?xml version="1.0" encoding="UTF-8"?>
<body>
<project identifier="project1">
    <author>Joe Smith</author>
    <author2>Rick Jones</author2>
    <path>projects/internal/project1</path>
    <version>1.51</version>
</project>
<project identifier="project2">
     <author>Terry Chimes</author>
     <author>Janie Jones</author>
     <path>projects/external/project2</path>
     <version>19.77</version>
 </project>
 </body>

Я добавил к вашему образцу XML-объявление версии/кодировки + высокоуровневый тег body, чтобы QXmlStreamReader не думал, что первый тег project является корневым элементом для всего документа. Я также изменил путь для первого проекта, чтобы он отличался от пути второго проекта.

И вот что я получил:

Path for project  "project1" :  "projects/internal/project1"
Path for project  "project2" :  "projects/external/project2"
person Dmitry    schedule 24.04.2018
comment
Спасибо за вклад. У меня нет никакого контроля над XML; оно исходит из Высших Мест. Это технически допустимо, хотя у него нет тега body. Я бы опубликовал свой код, но он был пронизан вызовами qDebug() и некоторой дополнительной сложностью, поскольку есть два других типа элементов, которые могут иметь элемент идентификатора. Это то, что нужно будет разбирать только изредка, поэтому я встал посреди ночи и написал свой собственный тупой парсер, используя пару регулярных выражений. Для этой цели работает достаточно хорошо. Спасибо за вашу помощь. - person Dito; 24.04.2018
comment
Невероятно достойный пример. Спасибо. - person developer01; 20.10.2020