Не удается идентифицировать пустые элементы с помощью 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>
Как видите, элементы с Id 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? И если так, как я могу обнаружить пустые элементы, которые не состоят из 2 отдельных элементов (начало / конец)?
Редактировать: Итак, Хейтард попросил у меня рабочий пример. Но его ответ привел меня к решению, которое почти ответило на мой вопрос. Поэтому я привел разъясненный пример в своем ответе.
2 ответа
Так что благодаря Хейтарду я понял, что это просто поведение QXmlStreamReader::readNextStartElement, которое как-то неожиданно. Я ожидал, что он действительно будет читать начальные элементы. И изначально я хотел заранее проверить, является ли элемент пустым, а затем решить, что делать с его содержимым. Кажется, что это невозможно. И эта невозможность покрыта документацией, которую я сам процитировал. Т.е. даже если атомарный элемент пуст, за ним фактически следует начальный элемент, который фактически является конечным элементом. Это плохо, так как вы не можете вернуться в поток.
Основываясь на его ответе, я написал небольшой пример, который разъясняет (извините) и отвечает на мой оригинальный вопрос.
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
Мне все еще не нравится это. Как это работает, мне нужно провести тройную проверку всего, что делает код менее читабельным.
Вы пробежали через это с помощью отладчика?
Я догадываюсь, что у вас есть ошибка в вашем коде - поэтому публикация полных и соответствующих частей вашего кода поможет исключить вероятность того, что это ошибка 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"
""