一、TinyXml的特点
TinyXml是一个基于DOM模型的、非验证的轻量级C 解释器。它是一个开源的解析XML的解析库,能够用于C++,能够在Windows或Linux中编译。这个解析库的模型通过解析XML文件,然后在内存中生成DOM模型,从而让我们很方便的遍历这棵XML树。
1. SAX和DOM
目前XML的解析主要有两大模型:SAX和DOM。
其中SAX是基于事件的,其基本工作流程是分析XML文档,当发现了一个新的元素时,产生一个对应事件,并调用相应的用户处理函数。这种方式占用内存少,速度快,但用户程序相应得会比较复杂。
而DOM(文档对象模型),则是在分析时,一次性的将整个XML文档进行分析,并在内存中形成对应的树结构,同时,向用户提供一系列的接口来访问和编辑该树结构。这种方式占用内存大,速度往往慢于SAX,但可以给用户提供一个面向对象的访问接口,对用户更为友好。
2. 验证和非验证
对 于一个特定的XML文档而言,其正确性分为两个层次。首先是其格式应该符合XML的基本格式要求,比如第一行要有声明,标签的嵌套层次必须前后一致等等, 符合这些要求的文件,就是一个合格的XML文件,称作well-formatted。但除此之外,一个XML文档因其内容的不同还必须在语义上符合相应的 标准,这些标准由相应的DTD文件或者Schema文件来定义,符合了这些定义要求的XML文件,称作valid。
因此,解析器也分为两种,一种是验证的,即会跟据XML文件中的声明,用相应的DTD文件对XML文件进行校验,检查它是否满足DTD文件的要求。另一种是忽略DTD文件,只要基本格式正确,就可以进行解析。
就我所知,验证的解析器通常都是比较重量级的。TinyXml不支持验证,但是体积很小,用在解析格式较为简单的XML文件,比如配置文件时,特别的合适。
二、构建与使用
1. 获取
TinyXml从这里可以找到最新版本的源代码,目前的版本是 2.6.2 (截至2011.11.01)。好久没有更新了。
在网上还有一个类同的TinyXml-2版本,github开源代码,其有在线html版本可以参阅。
2.构建
TinyXml 在构建时可以选择是否支持STL,选择的话,则可以使用std::string,所以通常应在Windows上,TinyXml的源码包里提供了VC6的 工程文件,直接用它就可以生成两个静该打开这个选项。态库(带STL和不带STL),非常容易。唯一需要注意的是,默认生成的库是单线程的,如果用在多线 程的项目中,需要改动一下配置,生成相应的多线程库。
3. 使用
构建了相应的库之后,在 使用了它们的工程中,只要在连接时把他们连上就行了。需要注意的是,如果需要STL支持,在编译用到了TinyXml的文件时,需要定义一个宏 TIXML_USE_STL,对gcc,可以使用参数-DTIXML_USE_STL,对cl.exe(VC),可以使用参数 /DTIXML_USE_STL,如果嫌麻烦,可以直接定义在 tinyxml.h文件里。
TinyXml它由两个头文件(.h文件)和四个CPP文件(.cpp文件)构成,用的时候,只要将(tinyxml.h、tinystr.h、tinystr.cpp、tinyxml.cpp、tinyxmlerror.cpp、tinyxmlparser.cpp)导入工程就可以用它的东西了。如果需要,可以将它做成自己的DLL来调用。
简单示例:
#include "tinyxml.h"
#include "tinystr.h"
#include <iostream>
using namespace std;
int main(int argc, char *argv[])
{
if(argc != 2)
{
cout << "usage: "<<argv[0] << " xmlfile" << endl;
return 1;
}
TiXmlDocument doc(argv[1]);
bool loadOk = doc.LoadFile();
if (!loadOk)
{
cout << "could load:" << doc.ErrorDesc() << endl;
}
TiXmlPrinter printer;//提供的工具类,目的是将xml的数据按格式输出
doc.Accept(&printer);
cout << printer.CStr() << endl;//输出
TiXmlElement*node = doc.FirstChildElement();//获取第一个element节点
cout << node->Value() << endl;//输出节点的值
string t;
node->QueryValueAttribute("type", &t);//获取节点属性
cout << t << endl;
doc.FirstChild()->NextSibling()->ToElement()->QueryStringAttribute("type", &t);//获取第二个子节点的数据
cout << "2:" << t << endl;
//使用遍历的方式进行处理
TiXmlNode* child = NULL;
TiXmlElement* element = NULL;
TiXmlAttribute *attr = NULL;
int ct;
while(child = doc.FirstChild()->IterateChildren(child))
{
cout << child->ValueStr() << "\t";
ct = child->Type();
cout << ct << "\t";
//根据不同的节点类型做相应处理
switch(ct)
{
case TiXmlNode::TINYXML_TEXT:
break;
case TiXmlNode::TINYXML_ELEMENT:
element = child->ToElement();
attr = element->FirstAttribute();
while(attr)
{
cout << attr->NameTStr() << "=" << attr->ValueStr() << '\t';
attr = attr->Next();
}
break;
}
}
return 0;
}
写XML示例代码:
void CPrintObj::WriteXML(CString &path)
{
TiXmlDocument* xmlDoc = new TiXmlDocument();
TiXmlElement element("AutoDraw");
TiXmlNode* rootNode = xmlDoc->InsertEndChild(element);
TiXmlNode* curNode = InsertNode(rootNode, "Paper");
WriteData(curNode, "Width", m_papp_data->ppaper_data->width);
WriteData(curNode, "Height", m_papp_data->ppaper_data->height);
WriteData(curNode, "FilterType", m_papp_data->m_nFileterType);
WriteData(curNode, "A0A1Lengthen", m_papp_data->m_nSelA0A1Length);
WriteData(curNode, "Code", m_papp_data->ppaper_data->code);
WriteData(curNode, "AutoDrawType", m_papp_data->auto_draw_style);
WriteData(curNode, "XCleanrance", m_papp_data->m_nXDrawingCleanrance);
WriteData(curNode, "YCleanrance", m_papp_data->m_nYDrawingCleanrance);
WriteText(curNode, "OutputPath", m_szUserSaveFilePath.GetBuffer());
WriteData(curNode, "IsSavePDF", m_papp_data->m_nPDF);
list_head *auto_draw_iter = &m_papp_data->auto_draw_list;
while((auto_draw_iter = auto_draw_iter->next) != &m_papp_data->auto_draw_list)
{
curNode = InsertNode(rootNode, "Page");
struct auto_draw_data *pauto_draw_data = list_entry(auto_draw_iter, struct auto_draw_data, list);
WriteText(curNode, "FilePath", pauto_draw_data->filename);
WriteData(curNode, "Width", pauto_draw_data->width);
WriteData(curNode, "Height", pauto_draw_data->height);
WriteData(curNode, "Code", pauto_draw_data->code);
WriteData(curNode, "A0A1Lengthen", pauto_draw_data->m_nA0A1_Lenthen);
}
char* filePath = WcharToChar(path);
xmlDoc->SaveFile(filePath);
xmlDoc->Clear();
delete xmlDoc;
delete filePath;
}
读XML示例代码:
void CDPrintObj::LoadData()
{
TCHAR szPath[MAX_PATH];
GetModuleFileName(NULL, szPath , MAX_PATH);
CString strFilePath = szPath;
strFilePath = strFilePath.Left(strFilePath.ReverseFind(_T('\\')));
CString strXmlFile;
strXmlFile.Format(_T("%s\\dumpdata.xml"), strFilePath);
TiXmlDocument *xmlDocument = new TiXmlDocument();
char strFile[MAX_PATH];
wstring2string(strXmlFile, strFile);
xmlDocument->LoadFile(strFile);
TiXmlHandle handle(xmlDocument);
swprintf_s(m_papp_data->dump_filename, L"%S", handle.FirstChild("/APP_DATA/DUMP_FILENAME").ToElement()->GetText());
m_papp_data->new_paper_size.totalheight = atoi( handle.FirstChild("/APP_DATA/TOTALHEIGHT").ToElement()->GetText());
TiXmlElement *pRootElement = handle.FirstChild("/APP_DATA/DRW_DATA").ToElement();
TiXmlElement *pElement = pRootElement->FirstChildElement();
while(pElement)
{
drw_data *pdrw_data = new drw_data;
swprintf_s(pdrw_data->filename, L"%S", pElement->FirstChildElement("FILENAME")->GetText());
list_add_tail(&pdrw_data->list, &m_papp_data->drw_list);
TiXmlElement *pSubElement = pElement->FirstChildElement("SHEET_DATA");
while(pSubElement)
{
sheet_data *psheet_data = new struct sheet_data;
list_add_tail(&psheet_data->list, &pdrw_data->sheet_list);
swprintf_s(psheet_data->sheet_name, L"%S", pSubElement->FirstChildElement("SHEET_NAME")->GetText());
psheet_data->data.width = atoi(pSubElement->FirstChildElement("WIDTH")->GetText());
psheet_data->data.height = atoi(pSubElement->FirstChildElement("HEIGHT")->GetText());
psheet_data->data.xpos = atof(pSubElement->FirstChildElement("XPOS")->GetText());
psheet_data->data.ypos = atof(pSubElement->FirstChildElement("YPOS")->GetText());
psheet_data->data.code = atoi(pSubElement->FirstChildElement("CODE")->GetText());
psheet_data->data.flip = atoi(pSubElement->FirstChildElement("FLIP")->GetText()) > 0;
pSubElement = pSubElement->NextSiblingElement();
}
pElement = pElement->NextSiblingElement();
}
}
三、 TinyXml的编程模型
1.类之间的关系
C++ TinyXML是个解析库,主要由DOM模型类(TiXmlBase、TiXmlNode、TiXmlAttribute、TiXmlComment、TiXmlDeclaration、TiXmlElement、TiXmlText、TiXmlUnknown)和操作类(TiXmlHandler)构成。如图所示。
TiXmlBase:其它类的基类,是个抽象类
TiXmlNode:表示一个节点,包含节点的一般方法,如访问自节点、兄弟节点、编辑自身、编辑子节点
TiXmlDocument:表示整个XML文档,不对应其中某个特定的节点。
TiXmlElement:表示元素节点,可以包含子节点和TiXmlAttribute
TiXmlComment:表示注释
TiXmlDeclaration:表示声明
TiXmlText:表示文本节点
TiXmlUnknown:表示未知节点,通常是出错了
TiXmlAttribute:表示一个元素的属性
可以看到TinyXml中的注释comment,声明declaration,元素element,文本等都是节点Node的子类,也就是说可以把XMl文件中的各个元素当做节点来处理。Node类型也有到各个子类之间的转换方法,如ToElement()转换成元素,ToDocument转换成文档等。
2. 需要注意的问题
2.1 各类之间的转换
由 于各个节点类都从TiXmlNode继承,在使用时常常需要将TiXmlNode*类型的指针转换为其派生类的指针,在进行这种转换时,应该首先使用由 TiXmlNode类提供的一系列转换函数,如ToElement(void),而不是c 的dynamic_cast
2.2 检查返回值
由于TinyXml是一个非校验的解析器,因此当解析一个文件时,很可能文件并不包含我们预期的某个节点,在这种情况下,TinyXml将返回空指针。因此,必须要对返回值进行检查,否则将很容易出现内存访问的错误。
四、总结
TinyXml 最大的特点就是它很小,可以很方便的静态连接到程序里。对于像配置文件、简单的数据文件这类文件的解析,它很适合。但是由于它是非验证的,因此需要在程序 里做许多检查工做,加重了程序编写的负担。因此对于复杂的XML文件,我觉得最好还是用验证的解析器来处理。