Почему я не могу проанализировать XML-файл с помощью QXmlStreamReader из Qt?

Я пытаюсь понять, как QXmlStreamReader работает для приложения на C++, которое я пишу. XML-файл, который я хочу разобрать, - это большой словарь со сложной структурой и множеством символов Юникода, поэтому я решил попробовать небольшой тестовый случай с более простым документом. К сожалению, я наткнулся на стену. Вот пример xml-файла:

<?xml version="1.0" encoding="UTF-8" ?>
<persons>
    <person>
        <firstname>John</firstname>
        <surname>Doe</surname>
        <email>john.doe@example.com</email>
        <website>http://en.wikipedia.org/wiki/John_Doe</website>
    </person>
    <person>
        <firstname>Jane</firstname>
        <surname>Doe</surname>
        <email>jane.doe@example.com</email>
        <website>http://en.wikipedia.org/wiki/John_Doe</website>
    </person>
    <person>
        <firstname>Matti</firstname>
        <surname>Meikäläinen</surname>
        <email>matti.meikalainen@example.com</email>
        <website>http://fi.wikipedia.org/wiki/Matti_Meikäläinen</website>
    </person>
</persons>

...и я пытаюсь разобрать его, используя этот код:

int main(int argc, char *argv[])
{
    if (argc != 2) return 1;

    QString filename(argv[1]);
    QTextStream cout(stdout);
    cout << "Starting... filename: " << filename << endl;

    QFile file(filename);
    bool open = file.open(QIODevice::ReadOnly | QIODevice::Text);
    if (!open) 
    {
        cout << "Couldn't open file" << endl;
        return 1;
    }
    else 
    {
        cout << "File opened OK" << endl;
    }

    QXmlStreamReader xml(&file);
    cout << "Encoding: " << xml.documentEncoding().toString() << endl;

    while (!xml.atEnd() && !xml.hasError()) 
    {
        xml.readNext();
        if (xml.isStartElement())
        {
            cout << "element name: '" << xml.name().toString() << "'" 
                << ", text: '" << xml.text().toString() << "'" << endl;
        }
        else if (xml.hasError())
        {
            cout << "XML error: " << xml.errorString() << endl;
        }
        else if (xml.atEnd())
        {
            cout << "Reached end, done" << endl;
        }
    }

    return 0;
}

...затем я получаю этот вывод:

C:xmltestDebug>xmltest.пример exe.в XML
Начало... имя файла: пример.в XML
Файл открыт OK
Кодировка:
Ошибка XML: обнаружен неправильно закодированный контент.

Что случилось? Этот файл не может быть проще, и он выглядит последовательным Для меня. С моим исходным файлом я также получаю пустую запись для кодировки, отображаются имена записей (), но, увы, текст () также пуст. Любые предложения очень ценятся, лично я глубоко озадачены.

5 ответов


я отвечаю на это сам, поскольку эта проблема была связана с тремя вопросами, два из которых были подняты ответами.

  1. файл не в кодировке UTF-8. Я изменил кодировку на iso-8859-1, и предупреждение о кодировке исчезло.
  2. функция text () работает не так, как я ожидал. Я должен использовать readElementText () для чтения содержимого записей.
  3. когда я пытаюсь readElementText() на элементе, который не содержит текста, например топ-уровня в моем случае парсер возвращает "данные персонажа" ошибка и разбор прерывается. Я нахожу это поведение странным (на мой взгляд, возвращение пустой строки и продолжение было бы лучше), но я думаю, что пока спецификация известна, я могу обойти ее и избежать вызова этой функции в каждой записи.

соответствующий раздел кода, который работает так, как ожидалось, теперь выглядит следующим образом:

while (!xml.atEnd() && !xml.hasError()) 
{
    xml.readNext();
    if (xml.isStartElement())
    {
        QString name = xml.name().toString();
        if (name == "firstname" || name == "surname" || 
            name == "email" || name == "website")
        {
            cout << "element name: '" << name  << "'" 
                         << ", text: '" << xml.readElementText() 
                         << "'" << endl;
        }
    }
}
if (xml.hasError())
{
    cout << "XML error: " << xml.errorString() << endl;
}
else if (xml.atEnd())
{
    cout << "Reached end, done" << endl;
}

файл не закодирован в UTF-8. Измените кодировку на iso-8859-1, и она будет разбираться без ошибок.

<?xml version="1.0" encoding="iso-8859-1" ?>

о кодировке: как сказал бейсмит и хмуэльнер, ваш файл, вероятно, неправильно закодирован (если только кодировка не потерялась при вставке его сюда). Попробуйте исправить это с помощью расширенного текстового редактора.

проблема с использованием text () заключается в том, что он не работает так, как вы ожидаете. text () возвращает содержимое текущего токена, если он имеет символы типа, комментарий, DTD или EntityReference. Ваш текущий токен-это StartElement, поэтому он пуст. Если хочешь ... потребляйте / читайте текст текущего startElement, вместо этого используйте readElementText ().


вы уверены, что ваш документ кодируется UTF-8? Какой Редактор вы используете? Проверьте, как выглядят символы ä, если вы просматриваете файл без декодирования.


попробуйте этот пример я только что скопировал его из моего проекта, он работает для меня.

void MainWindow::readXML(const QString &fileName)
{


fileName = "D:/read.xml";

QFile* file = new QFile(fileName);
if (!file->open(QIODevice::ReadOnly | QIODevice::Text))
{
     QMessageBox::critical(this, "QXSRExample::ReadXMLFile", "Couldn't open xml file", QMessageBox::Ok);
     return;
}

/* QXmlStreamReader takes any QIODevice. */
QXmlStreamReader xml(file);
/* We'll parse the XML until we reach end of it.*/
while(!xml.atEnd() && !xml.hasError())
{
    /* Read next element.*/
    QXmlStreamReader::TokenType token = xml.readNext();
    /* If token is just StartDocument, we'll go to next.*/
    if(token == QXmlStreamReader::StartDocument)
        continue;

    /* If token is StartElement, we'll see if we can read it.*/
    if(token == QXmlStreamReader::StartElement) {
        if(xml.name() == "email") {
            ui->listWidget->addItem("Element: "+xml.name().toString());
            continue;
        }
    }
}
/* Error handling. */
if(xml.hasError())
    QMessageBox::critical(this, "QXSRExample::parseXML", xml.errorString(), QMessageBox::Ok);

//resets its internal state to the initial state.
xml.clear();
}

void MainWindow::writeXML(const QString &fileName)
{
fileName = "D:/write.xml";
QFile file(fileName);
if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
{
     QMessageBox::critical(this, "QXSRExample::WriteXMLFile", "Couldn't open anna.xml", QMessageBox::Ok);
     return;
}
QXmlStreamWriter xmlWriter(&file);
xmlWriter.setAutoFormatting(true);
xmlWriter.writeStartDocument();
//add Elements
xmlWriter.writeStartElement("bookindex");
ui->listWidget->addItem("bookindex");
xmlWriter.writeStartElement("Suleman");
ui->listWidget->addItem("Suleman");

//write all elements in xml filexl
xmlWriter.writeEndDocument();
file.close();
if (file.error())
    QMessageBox::critical(this, "QXSRExample::parseXML", file.errorString(), QMessageBox::Ok);


}