1

我在使用 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 个单独元素(开始/结束)的空元素?

编辑: 所以 Huytard 要求我提供一个工作示例。但他的回答让我找到了一个几乎回答了我的问题的解决方案。因此,我在回答中提出了一个明确的例子。

4

2 回答 2

1

因此,感谢 Huytard,我认识到这只是 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

我还是不喜欢。这种工作方式我需要对所有降低代码可读性的内容进行三次检查。

于 2014-01-23T08:59:42.730 回答
0

你用调试器运行过这个吗?

我的预感是您的代码中存在错误 - 因此发布代码的完整和相关部分将有助于排除这是 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" 
"" 
于 2014-01-22T18:28:05.143 回答