Qt之XML文件解析SAX XML简介 和HTML 的语法很相似,但不同之处在于: HTML 被设计用来显示数据,其关注的是数据的外观,XML 被设计用来传输和存储数据,其关注的是数据的内容,因此,XML主要用来作为数据的存储和共享。
XML文档是一种树的结构,从根部扩展到枝叶。以下是一个XML示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <?xml version="1.0" encoding="UTF-8" ?> <root > <class name ="Rect" > <object name ="obj1" > <x1 > 10</x1 > <y1 > 10</y1 > <x2 > 50</x2 > <y1 > 50</y1 > <linewidth > 2</linewidth > <scale > 0</scale > <rotate > 0</rotate > </object > </class > </root >
其中第一行 是XML 声明。它定义 XML 的版本和所使用的编码格式,<root> </root>
为根节点的起始(在XML中可以自定义节点名称),<class> </class>
为子元素,其中name
为其属性,值为Rect
;每一个子元素都可以拥有子元素,故class
的子元素为object
,依次类推; 所有的元素都可以有文本内容和属性,如x1的文本为10,x2的文本为50。
写XML文件 使用QT自带的模块QXmlStreamWriter
1. 构造函数 QXmlStreamWriter有三种构造方式,分别是
1 2 3 QXmlStreamWriter::QXmlStreamWriter ([QString](qstring.html) **string*); QXmlStreamWriter::QXmlStreamWriter ([QByteArray](qbytearray.html) **array*); QXmlStreamWriter::QXmlStreamWriter ([QIODevice](qiodevice.html) **device*)
第一种和第二种分别是创建一个QString和QByteArray对象,然后将xml流写入到其中,第三种则是创建一个文件设备将xml流写入其中,一般来说,第三种常用。
2. 属性 autoFormatting :bool量,表示是否自动格式化文档,其相关读写函数为:
1 2 void setAutoFormatting (bool enable) bool autoFormatting () const
autoFormattingIndent :int变量,当自动格式化为真时,它表示缩进的空格或者制表符的数量,相关读写函数为:
1 2 int autoFormattingIndent () const void setAutoFormattingIndent (int spacesOrTabs)
一般4个空格等于一个制表符,也就是说setAutoFormattingIndent(4)
的效果与setAutoFormattingIndent(-1)
的效果是一样的。
3. 方法 这里只介绍常用的一些方法。
1、自动格式化代码
1 void QXmlStreamWriter::setAutoFormatting (bool enable)
2、 设置文档编码
1 2 void QXmlStreamWriter::setCodec (QTextCodec *codec) ;void QXmlStreamWriter::setCodec (const char *codecName)
默认是utf-8
,其它支持的编码格式如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 Big5 Big5-HKSCS CP949 EUC-JP EUC-KR GB18030 HP-ROMAN8 IBM 850 IBM 866 IBM 874 ISO 2022-JP ISO 8859-1 to 10 ISO 8859-13 to 16 Iscii-Bng, Dev, Gjr, Knd, Mlm, Ori, Pnj, Tlg, and Tml KOI8-R KOI8-U Macintosh Shift-JIS TIS-620 TSCII UTF-8 UTF-16 UTF-16BE UTF-16LE UTF-32 UTF-32BE UTF-32LE Windows-1250 to 1258
3、设置自动缩进值
1 void setAutoFormattingIndent (int spacesOrTabs)
4、开始写xml文档时,需要先设置xml的版本和所使用的编码,一般为1.0版本,编码为utf-8,相关函数为:
1 2 3 4 void QXmlStreamWriter::writeStartDocument () ;void QXmlStreamWriter::writeStartDocument (const QString &version) ;void QXmlStreamWriter::writeStartDocument (const QString &version, bool standalone)
与其相对应的,关闭文档写入需要调用函数
1 2 void QXmlStreamWriter::writeEndDocument () ;
5、xml文档为树结构,其有一个根节点,根节点下面都挂在着许多元素,节点 和元素 设置都为以下函数
1 2 3 void QXmlStreamWriter::writeStartElement (const QString &namespaceUri, const QString &name) ;void QXmlStreamWriter::writeStartElement (const QString &qualifiedName)
与其相对应的,关闭节点或元素需要调用函数
1 2 void QXmlStreamWriter::writeEndElement ()
6、每一个元素都可以有若干个属性,写入带有名称和值的属性
1 2 3 4 5 void QXmlStreamWriter::writeAttribute (const QString &namespaceUri, const QString &name, const QString &value) ;void QXmlStreamWriter::writeAttribute (const QString &qualifiedName, const QString &value) ;void QXmlStreamWriter::writeAttribute (const QXmlStreamAttribute &attribute) ;void QXmlStreamWriter::writeAttributes (const QXmlStreamAttributes &attributes) ;
7、写文本
1 2 void QXmlStreamWriter::writeCharacters (const QString &text) ;
8、写入带有名称和文本的元素
1 2 3 void QXmlStreamWriter::writeTextElement (const QString &namespaceUri, const QString &name, const QString &text) ;void QXmlStreamWriter::writeTextElement (const QString &qualifiedName, const QString &text) ;
9、写注释
1 2 void QXmlStreamWriter::writeComment (const QString &text) ;
还有一些方法就不一一介绍了,以上已经可以写出一个完整的xml文档。
4. 示例 将一些图元的信息写入到xml文件中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 void MainWindow::on_btnWrite_clicked () { QString fileName = QFileDialog::getSaveFileName (this , "Save File" , "./untitled.xml" ,"Xml(*.xml)" ); qDebug () << fileName; QFile file (fileName) ; if (!file.open (QIODevice::WriteOnly)) { qDebug () << "Save failed" ; return ; } QXmlStreamWriter writer (&file) ; writer.setCodec ("utf-8" ); writer.writeStartDocument ("1.0" ); writer.setAutoFormatting (true ); writer.setAutoFormattingIndent (-1 ); writer.writeStartElement ("root" ); writer.writeComment ("写入矩形图元的信息" ); writer.writeStartElement ("class" ); writer.writeAttribute ("name" , "Rect" ); writer.writeStartElement ("object" ); writer.writeAttribute ("name" , "obj1" ); writer.writeTextElement ("x1" , QString::number (10 )); writer.writeTextElement ("y1" , QString::number (10 )); writer.writeTextElement ("x2" , QString::number (50 )); writer.writeTextElement ("y1" , QString::number (50 )); writer.writeTextElement ("linewidth" , QString::number (2 )); writer.writeTextElement ("scale" , QString::number (0 )); writer.writeTextElement ("rotate" , QString::number (0 )); writer.writeEndElement (); writer.writeStartElement ("object" ); writer.writeAttribute ("name" , "obj2" ); writer.writeTextElement ("x1" , QString::number (20 )); writer.writeTextElement ("y1" , QString::number (30 )); writer.writeTextElement ("x2" , QString::number (50 )); writer.writeTextElement ("y1" , QString::number (60 )); writer.writeTextElement ("linewidth" , QString::number (2 )); writer.writeTextElement ("scale" , QString::number (0 )); writer.writeTextElement ("rotate" , QString::number (90 )); writer.writeEndElement (); writer.writeEndElement (); writer.writeComment ("写入线图元的信息" ); writer.writeStartElement ("class" ); writer.writeAttribute ("name" , "Line" ); writer.writeStartElement ("object" ); writer.writeAttribute ("name" , "obj1" ); writer.writeTextElement ("x1" , QString::number (10 )); writer.writeTextElement ("y1" , QString::number (10 )); writer.writeTextElement ("x2" , QString::number (50 )); writer.writeTextElement ("y1" , QString::number (50 )); writer.writeTextElement ("linewidth" , QString::number (2 )); writer.writeTextElement ("scale" , QString::number (0 )); writer.writeTextElement ("rotate" , QString::number (0 )); writer.writeEndElement (); writer.writeStartElement ("object" ); writer.writeAttribute ("name" , "obj2" ); writer.writeTextElement ("x1" , QString::number (10 )); writer.writeTextElement ("y1" , QString::number (10 )); writer.writeTextElement ("x2" , QString::number (50 )); writer.writeTextElement ("y1" , QString::number (50 )); writer.writeTextElement ("linewidth" , QString::number (2 )); writer.writeTextElement ("scale" , QString::number (0 )); writer.writeTextElement ("rotate" , QString::number (0 )); writer.writeEndElement (); writer.writeEndElement (); writer.writeComment ("写入椭圆图元信息" ); writer.writeStartElement ("class" ); writer.writeAttribute ("name" , "Ellipse" ); writer.writeCharacters ("无椭圆图元" ); writer.writeEndElement (); writer.writeEndElement (); writer.writeEndDocument (); file.close (); }
最后构成的xml文档如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 <?xml version="1.0" encoding="UTF-8" ?> <root > <class name ="Rect" > <object name ="obj1" > <x1 > 10</x1 > <y1 > 10</y1 > <x2 > 50</x2 > <y1 > 50</y1 > <linewidth > 2</linewidth > <scale > 0</scale > <rotate > 0</rotate > </object > <object name ="obj2" > <x1 > 20</x1 > <y1 > 30</y1 > <x2 > 50</x2 > <y1 > 60</y1 > <linewidth > 2</linewidth > <scale > 0</scale > <rotate > 90</rotate > </object > </class > <class name ="Line" > <object name ="obj1" > <x1 > 10</x1 > <y1 > 10</y1 > <x2 > 50</x2 > <y1 > 50</y1 > <linewidth > 2</linewidth > <scale > 0</scale > <rotate > 0</rotate > </object > <object name ="obj2" > <x1 > 10</x1 > <y1 > 10</y1 > <x2 > 50</x2 > <y1 > 50</y1 > <linewidth > 2</linewidth > <scale > 0</scale > <rotate > 0</rotate > </object > </class > <class name ="Ellipse" > 无椭圆图元</class > </root >
读XML文件 使用QT自带的模块QXmlStreamReader
1. 构造函数 可以通过
1 2 3 4 5 6 QXmlStreamReader (const char *data)QXmlStreamReader (const QString &data)QXmlStreamReader (const QByteArray &data)QXmlStreamReader (QIODevice *device)QXmlStreamReader ()
2. 属性 namespaceProcessing :bool变量,指是否处理命名空间,相关读写函数为
1 2 3 bool namespaceProcessing () const void setNamespaceProcessing (bool )
3. 方法 1、设置文件设备,如果你使用了无参的构造,则可以通过下面这个函数来设置要读取的文件
1 2 void setDevice (QIODevice *device)
2、读取下一个下一个标记
1 2 QXmlStreamReader::TokenType QXmlStreamReader::readNext () ;
读取xml文档主要就用这一个函数,通过其返回值 QXmlStreamReader::TokenType
来判断读取的是什么元素,枚举TokenType
如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 enum TokenType { NoToken = 0 , Invalid, StartDocument, EndDocument, StartElement, EndElement, Characters, Comment, DTD, EntityReference, ProcessingInstruction };
NoToken:没有读取到任何信息 Invalid:读取失败,错误信息保存在error()
和errorString()
StartDocument:读取到的为xml文档版本号documentVersion()
和编码格式documentEncoding()
EndDocument:读取到文档结束的位置 StartElement:读取到的为一个节点,节点命名空间和名字分别保存在namespaceUri()
和name()
中,其属性值保存在attribute()
中。 EndElement:读取到该节点的结束位置 Characters:读取的内容为字符,保存在text()
中 Comment:读取的内容为注释,保存在text()
中 DTD:读取的内容为DTD,保存在text()
中 EntityReference:无法解析的实体引用 ProcessingInstruction 3、读取开始节点中的文本
1 2 QString QXmlStreamReader::readElementText (QXmlStreamReader::ReadElementTextBehaviour behaviour = ErrorOnUnexpectedElement)
这个函数用在当读取的标记为StartElement
之后,直接调用这个函数可以更方便的获取其内容
示例 读取上面写入的文档,示例代码为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 void MainWindow::on_btnRead_clicked () { QString fileName = QFileDialog::getOpenFileName (this , "Save this file As" , "./" , "Xml(*.xml)" ); qDebug () << fileName; QFile file (fileName) ; if (!file.open (QIODevice::ReadOnly)) { qDebug () << "Save failed" ; return ; } QXmlStreamReader reader (&file) ; QXmlStreamReader::TokenType token; QXmlStreamAttributes attribute; QString str, attrStr; while (!reader.atEnd ()) { token = reader.readNext (); if (token == QXmlStreamReader::StartElement) { str = reader.name ().toString (); if (QString::compare (str, "root" ) == 0 ) { qDebug () << "这是根节点:" << str; qDebug () << "----------------" ; } else if (QString::compare (str, "class" ) == 0 ) { attribute = reader.attributes (); if (attribute.hasAttribute ("name" )) { attrStr.clear (); attrStr = attribute.value ("name" ).toString (); } qDebug () << "************************************************" ; qDebug () << "这是元素:" << str << ", 属性为" << attrStr; } else if (!QString::compare (str, "object" )) { attribute = reader.attributes (); if (attribute.hasAttribute ("name" )) { attrStr.clear (); attrStr = attribute.value ("name" ).toString (); } qDebug () << "这是元素:" << str << ", 属性为" << attrStr; } else if (QString::compare (str, "x1" ) == 0 ) { qDebug () << attrStr << "对象的x1值为:" << reader.readElementText (); } else if (QString::compare (str, "x2" ) == 0 ) { qDebug () << attrStr << "对象的x2值为:" << reader.readElementText (); } else if (QString::compare (str, "y1" ) == 0 ) { qDebug () << attrStr << "对象的y1值为:" << reader.readElementText (); } else if (QString::compare (str, "y2" ) == 0 ) { qDebug () << attrStr << "对象的y2值为:" << reader.readElementText (); } else if (QString::compare (str, "linewidth" ) == 0 ) { qDebug () << attrStr << "对象的linewidth值为:" << reader.readElementText (); } else if (QString::compare (str, "scale" ) == 0 ) { qDebug () << attrStr << "对象的scale值为:" << reader.readElementText (); } else if (QString::compare (str, "rotate" ) == 0 ) { qDebug () << attrStr << "对象的rotate值为:" << reader.readElementText (); qDebug () << "----------------" ; } } else if (token == QXmlStreamReader::Characters) { } else if (token == QXmlStreamReader::Comment) { qDebug () << reader.text ().toString (); } else if (token == QXmlStreamReader::Invalid) { qDebug () << "读取失败:" << reader.errorString (); } } }
打印输出结果如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 "H:/robot_plus/tests/Xml/XML/bin/untitled1.xml" 这是根节点: "root" ---------------- "写入矩形图元的信息" ************************************************ 这是元素: "class" , 属性为 "Rect" 这是元素: "object" , 属性为 "obj1" "obj1" 对象的x1值为: "10" "obj1" 对象的y1值为: "10" "obj1" 对象的x2值为: "50" "obj1" 对象的y1值为: "50" "obj1" 对象的linewidth值为: "2" "obj1" 对象的scale值为: "0" "obj1" 对象的rotate值为: "0" ---------------- 这是元素: "object" , 属性为 "obj2" "obj2" 对象的x1值为: "20" "obj2" 对象的y1值为: "30" "obj2" 对象的x2值为: "50" "obj2" 对象的y1值为: "60" "obj2" 对象的linewidth值为: "2" "obj2" 对象的scale值为: "0" "obj2" 对象的rotate值为: "90" ---------------- "写入线图元的信息" ************************************************ 这是元素: "class" , 属性为 "Line" 这是元素: "object" , 属性为 "obj1" "obj1" 对象的x1值为: "10" "obj1" 对象的y1值为: "10" "obj1" 对象的x2值为: "50" "obj1" 对象的y1值为: "50" "obj1" 对象的linewidth值为: "2" "obj1" 对象的scale值为: "0" "obj1" 对象的rotate值为: "0" ---------------- 这是元素: "object" , 属性为 "obj2" "obj2" 对象的x1值为: "10" "obj2" 对象的y1值为: "10" "obj2" 对象的x2值为: "50" "obj2" 对象的y1值为: "50" "obj2" 对象的linewidth值为: "2" "obj2" 对象的scale值为: "0" "obj2" 对象的rotate值为: "0" ---------------- "写入椭圆图元信息" ************************************************ 这是元素: "class" , 属性为 "Ellipse"
总结 使用SAX方式进行读写XML文档更易上手,也更符合XML标准,不足之处在于仅支持迭代读写,如果你想要更改某个属性,只能重新开始写入。使用DOM 方式进行解析则可以达到增删改查的操作。但是两者各有利弊。