Как можно разобрать строку, подобную xml, и преобразовать ее в отдельный список?

Я пытаюсь преобразовать следующую строку:

<Categories>
  <Category Assigned="0">
    6 Level
    <Category Assigned="1">
      6.2 Level
      <Category Assigned="0">
        6.3 Level
        <Category Assigned="0">
          6.4 Level
          <Category Assigned="1">
            6.5 Level
          </Category>
        </Category>
      </Category>
    </Category>
  </Category>
</Categories>

В отдельный список, например:

6 Level/6.2 Level/6.3 Level/6.4 Level/6.5 Level, 6 Level/6.2 Level

Робин Миллс из exiv2 предоставил perl-скрипт: http://dev.exiv2.org/boards/3 /topics/1912?r=1923#сообщение-1923

Это также должно проанализировать Assigned="1". Как это можно сделать на C++ для использования в digikam, внутри dmetadata.cpp со структурой вида:

    QStringList ntp = tagsPath.replaceInStrings("<Category Assigned="0">", "/");

У меня недостаточно опыта программирования, чтобы понять это, и я не нашел в Интернете ни одного образца кода, который делает что-то подобное. Я также хотел бы включить код в сам exiv2, чтобы другие приложения могли извлечь выгоду.

Рабочий код будет включен в дигикам: https://bugs.kde.org/show_bug.cgi ?id=345220

1
asp 11 Апр 2015 в 21:11
Действительно ли элементы Category вложены таким образом? это очень необычно
 – 
Borodin
11 Апр 2015 в 21:18
Довольно необычно, да. Вот как работает ACDSee.
 – 
asp
11 Апр 2015 в 21:24

2 ответа

Код, который вы связали, использует модуль Perl XML::Parser::Expat, который является связующим слоем поверх Expat Джеймса Кларка. Анализатор XML.

Если вы хотите следовать тем же маршрутом, вам следует написать C++, использующий ту же библиотеку, но его использование может быть неуклюжим, поскольку API осуществляется через обратные вызовы, которые вы указываете для вызова при возникновении определенных событий во входящем потоке XML. Вы можете увидеть их в коде Perl, в комментариях process an start-of-element event и т.д.

После того, как вы связались с библиотекой, должно быть просто написать код C, который эквивалентен Perl в обратных вызовах — каждый из них состоит только из одной строки. Пожалуйста, откройте новый вопрос, если у вас возникли проблемы с пониманием Perl.

Также обратите внимание, что Expat — это парсер без проверки, который пропускает искаженные данные без комментариев.

Учитывая, что самой большой задачей является анализ XML-данных в первую очередь, вы можете предпочесть другое решение, которое позволит вам построить структуру документа в памяти из XML-данных и опрашивать ее с помощью Объектная модель документа (DOM). Библиотека libxml позволяет вам сделать это и имеет собственный связующий слой Perl в < модуль href="https://metacpan.org/pod/XML::LibXML" rel="nofollow noreferrer">XML::LibXML

0
Community 20 Июн 2020 в 12:12
Я собираюсь изменить вопрос, чтобы исключить преобразование perl, так как это не важная часть.
 – 
asp
11 Апр 2015 в 23:38
@asp: Хм, хорошо. Но что является важной частью? Я думаю, что было бы неплохо дать ссылку на код Perl, чтобы показать алгоритм нужного вам преобразования. Но если вы хотите описать этот алгоритм без ссылки на реализацию Perl, то это может быть шагом назад, особенно если вы не знакомы с Perl.
 – 
Borodin
11 Апр 2015 в 23:49
Важной частью является преобразование строки, а не пример Perl.
 – 
asp
13 Апр 2015 в 04:55
Я думаю, что решение остается прежним: выберите библиотеку анализатора XML и напишите некоторый код, который использует ее для анализа XML и извлечения текстовых узлов.
 – 
Borodin
13 Апр 2015 в 16:34
Спасибо @Бородин. Я ищу примеры, которые делают такие вещи. Жиль из digikam говорит, что это возможно с помощью QstringList...
 – 
asp
13 Апр 2015 в 20:12

Maik Qualmann предоставил рабочий патч для digikam!

QString xmlACDSee = getXmpTagString("Xmp.acdsee.categories", false);
if (!xmlACDSee.isEmpty())
{
    xmlACDSee.remove("</Categories>");
    xmlACDSee.remove("<Categories>");
    xmlACDSee.replace("/", "|");

    QStringList tagsXml = xmlACDSee.split("<Category Assigned");
    int category        = 0;
    int length;
    int count;

    foreach(const QString& tags, tagsXml)
    {
        if (!tags.isEmpty())
        {
            count  = tags.count("<|Category>");
            length = tags.length() - (11 * count) - 5;

            if (category == 0)
            {
                tagsPath << tags.mid(5, length);
            }
            else
            {
                tagsPath.last().append(QString("/") + tags.mid(5, length));
            }

            category = category - count + 1;

            if (tags.left(5) == QString("=\"1\">") && category > 0)
            {
                tagsPath << tagsPath.value(tagsPath.size() - count - 1);
            }
        }
    }

    if (!tagsPath.isEmpty())
    {
        return true;
    }
}
0
asp 18 Апр 2015 в 17:49