Не удается идентифицировать пустые элементы с помощью QXmlStreamReader

У меня возникли проблемы с обнаружением пустых элементов с помощью Qt QXmlStreamReader (Qt 4.8.1). Существует файл XML со следующим разделом

<Groups Number="4">
  <Group Id="0" GroupName="Chambers">
    <MemberChannels>4,5,6,7,8,9,10,11</MemberChannels>
    <AverageShown>true</AverageShown>
  </Group>
  <Group Id="1" GroupName="Fluids">
    <MemberChannels>0,1,17,18</MemberChannels>
    <AverageShown>false</AverageShown>
  </Group>
  <Group Id="2"/>
  <Group Id="3"/>
</Groups>

Как видите, элементы с идентификаторами 2 и 3 пусты, кроме атрибута. Атрибут ничего не меняет. Если дело не в элементе, проблема все равно возникает.

Это код парсинга с использованием QXmlStreamReader, я его упростил, поэтому он может не скомпилироваться. Просто вы поняли основную идею.

[...]
QXmlStreamReader* m_poStreamReader = new QXmlStreamReader;
[...]

if(m_poStreamReader->readNextStartElement() && m_poStreamReader->name().toString() == "Group") {
  this->parseGroupElement();
}

[...]

bool CTempscanXmlParser::parseGroupElement( void ) {
  TGroupElement tElement;
  if(m_poStreamReader->isStartElement() && !m_poStreamReader->isEndElement()) { // not empty
    TGroupElement tElement = this->readGroupElement();
  } else if(m_poStreamReader->isStartElement() && m_poStreamReader->isEndElement()) { // empty
    tElement.oGroupName = QString::null;
  }
  [...]
}

В документации говорится:

О пустых элементах также сообщается как StartElement, за которым непосредственно следует EndElement.

Я могу использовать readNext() и все равно не получить конечный элемент. Похоже, что парсер способен обнаружить только

<tag></tag>

как пустой элемент, но не

<tag/>

Итак, это только у меня или проблема существует в Qt? И если да, то как я могу обнаружить пустые элементы, которые не состоят из двух отдельных элементов (начало/конец)?

Редактировать: Хьютард попросил меня привести рабочий пример. Но его ответ привел меня к решению, которое почти ответило на мой вопрос. Поэтому я привожу разъясненный пример в своем ответе.


person HJo    schedule 22.01.2014    source источник
comment
Что тогда возвращает readNext(), если это не EndElement?   -  person Frank Osterfeld    schedule 22.01.2014
comment
Он возвращает QXmlStreamReader::Characters.   -  person HJo    schedule 22.01.2014


Ответы (2)


Итак, благодаря Хьютарду я понял, что это просто поведение QXmlStreamReader::readNextStartElement, которое несколько неожиданно. Я ожидал, что он действительно просто прочитает элементы start. И изначально я хотел заранее проверить, пуст ли элемент, а затем решить, что делать с его содержимым. Кажется, что это невозможно. И эта невозможность покрывается документацией, которую я сам цитировал. т.е. даже если атомарный элемент пуст, за ним фактически следует начальный элемент, который на самом деле является конечным элементом. Это плохо, так как вы не можете вернуться в поток.

Основываясь на его ответе, я написал небольшой пример, который разъясняет (извините) и отвечает на мой первоначальный вопрос.

  const QString XML_STR = "<Groups Number=\"4\">" \
                          "<Group Id=\"0\" GroupName=\"Chambers\">" \
                          "<MemberChannels>4,5,6,7,8,9,10,11</MemberChannels>" \
                          "<AverageShown>true</AverageShown>" \
                          "</Group>" \
                          "<Group Id=\"1\"/>" \
                          "<Group Id=\"2\"/>" \
                          "</Groups>";

  int main(int /* argc */, char** /* argv[] */)
  {

     qDebug() << "the way it would have made sense to me:";
     {
        QXmlStreamReader reader(XML_STR);

        while(!reader.atEnd())
        {
           reader.readNextStartElement();
           QString comment = (reader.isEndElement()) ? "is empty" : "has children";
           qDebug() << reader.name() << comment;
        }
     }

    qDebug() << "\napproximation to the way it should probably be done:";
    {
       QXmlStreamReader reader(XML_STR);

       bool gotoNext = true;
       while(!reader.atEnd())
       {
          if(gotoNext) {
             reader.readNextStartElement();
          }
          QString output = reader.name().toString();
          reader.readNext();
          if(reader.isEndElement()) {
             output += " is empty";
             gotoNext = true;
          } else {
             output += " has children";
             gotoNext = false;
          }
          qDebug() << output;
       }
    }

    return 0;
  }

что приводит к следующему выводу

  #they way it would have made sense to me: 
  "Groups" "has children" 
  "Group" "has children" 
  "MemberChannels" "has children" 
  "MemberChannels" "is empty" 
  "AverageShown" "has children" 
  "AverageShown" "is empty" 
  "Group" "is empty" 
  "Group" "has children" 
  "Group" "is empty" 
  "Group" "has children" 
  "Group" "is empty" 
  "Groups" "is empty" 
  "" "has children"
  # this has all been plain wrong

  #approximation to the way it should probably be done: 
  "Groups has children" 
  "Group has children" 
  "MemberChannels has children" 
  " is empty"           # ... but not a start element
  "AverageShown has children" 
  " is empty" 
  "Group has children"  # still wrong! this is an end element
  "Group is empty" 
  "Group is empty" 
  "Groups has children" # ditto

Мне все еще это не нравится. Как это работает, мне нужно трижды проверить все, что делает код менее читаемым.

person HJo    schedule 23.01.2014

Вы проходили это через отладчик?

Я предполагаю, что у вас есть ошибка в вашем коде. Поэтому публикация полных и соответствующих частей вашего кода поможет исключить возможность того, что это ошибка Qt (на что я готов поспорить). нет).

Этот короткий тест ниже должен подтвердить, что QXmlStreamReader может анализировать пустой элемент:

const QString XML_STR = "<root><node /></root>";

int main(int /* argc */, char* /* argv[] */)
{

  qDebug() << "Using readNextStartElement():";
  {
    QXmlStreamReader reader(XML_STR);

    while(!reader.atEnd())
    {
      reader.readNextStartElement();
      qDebug() << reader.name();
    }
  }

  qDebug() << "Using readNext():";
  {
    QXmlStreamReader reader(XML_STR);

    while(!reader.atEnd())
    {
      reader.readNext();
      qDebug() << reader.name();
    }
  }

  return 0;
}

Выход:

Using readNextStartElement(): 
"root" 
"node" 
"node" 
"root" 
"" 
Using readNext(): 
"" 
"root" 
"node" 
"node" 
"root" 
"" 
person Huy    schedule 22.01.2014
comment
Где ваш код различает пустые элементы и два следующих друг за другом элемента одного типа? Разве не неправильно, что есть два элемента start с узлом имени, хотя на самом деле их всего один? Где ваш тест для элемента end, чтобы проверить, пуст ли он? Я постараюсь обновить вопрос как можно скорее с более обширным примером. - person HJo; 23.01.2014