简单易懂的XML parsing——Qt篇
关于XML的文章我先前写过一篇。之后就再也没写过。原因是很简单的。虽然那篇文章是用Matlab的代码说明XML解析,但是XML的基本概念都是一致的,我也没必要再就C++或是Python等语言再写一遍在其他语言下面怎么用其他的库解析XML,都是大同小异。
可是这个世界上奇葩比较多。最近在做《网络通信原理》的project的时候,用到了Qt里面的QXmlStreamReader。有意思的是,这个东西不按常理出牌。为说明这个特性,我引用Qt 5关于QXmlStreamReader上面的一段话:
QXmlStreamReader is an incremental parser. It can handle the case where the document can’t be parsed all at once because it arrives in chunks (e.g. from multiple files, or over a network connection).
…
QXmlStreamReader is memory-conservative by design, since it doesn’t store the entire XML document tree in memory, but only the current token at the time it is reported.
由这段话我们可以看出,QXmlStreamReader的一个重要特点是,它是一个增量parser。QXmlStreamReader有一个特别的构造函数QXmlStreamReader::QXmlStreamReader(QIODevice * device)
,这个device可以是QNetworkReply也可以是QFile。相信这样的好处大家都可以看得出来。为了应付不同IODevice的特性,QXmlStreamReader也只能采取增量解析的方法。然后又有了下面的概念:token.
QXmlStreamReader不在内存中保存全部的DOM tree,现在解析的位置和所解析的对象用token说明。关于什么是token,其实我也不知道。但是QXmlStreamReader提供了一个函数:TokenType QXmlStreamReader::readNext()
,有关这个函数的说明是“Reads the next token and returns its type.”
按照官方文档上面的解释,一个可行的解析模型可以是这样:
1 | QXmlStreamReader xml; |
由此可见,QXmlStreamReader在解析xml的时候,以token为单位解析xml文档数据。
我在上一篇文章中讲过,xml有Element node,Element node以Text node作为child,Attribute node从属于Element node,comment node相对独立,而以上四种node都由document node生成,document node可以说是一个xml文档的代表,xml parsing的核心是element node。但是在Qt中,token与这种标准的概念似乎完全无关。它更关心我现在读到的东西是什么。在TokenType的定义中,一共给出了9种不同token的定义,而判断当前parser的tokenType是什么的函数一共有十二种。
我们可以想象,这种parser,一块一块地读取xml文档,只前进不后退,每一块代表一种既定的token,直到全部读完xml为止(也就是atEnd()
为真的时候)。
下面让我展示一段我这个project中的一段代码:
1 | if(reply->error()==QNetworkReply::NoError){ |
xml文档格式如下:
1 | <articles> |
代码中reply是个API请求的回应,我的目的是吧这个回应中的每一条信息存放在articlelist中。值得注意的是14-16行那段代码,由于这个是一个增量parser,我们不能使用
1 | ui->listWidget->addItem(xml.readElementText()); |
否则record.title
将为空。