张冲
摘 要:XML是一种仅基于普通文本却可以实现任何两个应该程序间的数据共享的文本语言。本文在简单介绍XML的基础上,通过c++解析器Tingxml实现c++对多值多属性XML文件的读取和存储,并对Tingxml解析器在ASCII(多字节字符集)项目中读取XML过程中出现的不支持中文字符问题进行解决。
关键词:XML;Tingxml;中文字符;STL
1 XML简介
可扩展性标记语言(Extensible Markup Language,简称XML)是W3C(World Wide Web Consortium)定义的一种程序间数据传输和交换的标准语言,为不同应用程序间的数据传输和交换提供了一种简单的方法[1]。XML独立于任何语言和体系结构,是一种灵活的不局限于任何特定模式的半结构化文本数据,无需顾虑格式,只要求标签的成对出现并按照树状结构存储即可,所以能很好的表达数据本身,适合进行数据的存储和传递,可以用来描述各种复杂信息。
2 C++XML解析器(Tingxml)
XML的解析技术作为其应用的基础,一直是XML的研究热点[2]。由于c++中本身未包含解析XML的函数库,所以当c++需要对XML进行解析时常使用c++XML解析器来完成。
本文主要介绍基于Tingxml解析器的c++XML解析,Tingxml是一个基于DOM模型的轻量级XML解析器,其模型通过解析XML,在内存中生成DOM模型,将整个文档分成多个元素(如书、章、节、段等),并利用树型结构表示这些元素之间的顺序关系以及嵌套包含关系,从而让我们很方便的遍历这棵XML树。
整个Tingxml解析库的构成主要由DOM模型类和操作类构成。DOM模型类包括:TiXMLBase(模型基类)、TiXMLAttribute(元素属性类)、TiXMLNode(应用于DOM结构节点)、TiXMLComment(XML中的注释类)、TiXMLDeclaration(应用于XML的声明,即<?versiong="1.0" ?> )、TiXMLDocument(文档类)、TiXMLElement(元素类)、TiXMLText(文字类)、TiXMLUnknown(应用于XML的位置部分)。操作类主要是TiXMLHandler类,定义了对XML的操作。Tingxml由两个头文件(.h文件)和四个CPP文件(.cpp文件)构成,只要将其导入工程就可以使用其进行XML的解析。
3 XML的数据解析
(1)含多种类型数据XML的数据解析
考虑到XML包含数据的多样性并结合Tingxml解析库自身的特点,本文利用STL(标准模板库)中的顺序存储容器(vector)、关联存储容器(map)和结构体相互嵌套的方法来对解析得到的数据进行存储,实现对多值多属性XML文档的数据存储。
STL(标准模板库)是C++开发中用于数据存储所必不可少的函数库,其中的存储容器包括:向量(vector);列表(list);集合(set);映射(map)等[3]。
在对XML进行数据解析的时候,基于XML的特性,我们很容易可以想到去使用关联容器来对解析出来的数据进行存储。但STL库中的关联容器映射(map)中的key/value存储方式仅能满足数据名和数据值得存储,当XML的一个节点上含有多个属性和多个值时仅用map是无法满足使用需要的。为此,本文采用vector、结构体和map相嵌套的方法实现单个节点含有多个属性和多个值的XML的解析,并将数据的属性和值分开存储以方便使用。具体实现方法如下:
struct arg1//创建一个包含两个map
{map
map
vector
ReadXML(const char* XMLpath)
{arg1 d_arg;
TiXMLDocument myDocument(XMLpath); //根据路径获取整个XML文档
myDocument.LoadFile();//下载xml文档
TiXMLElement* rootElement = myDocument.RootElement(); //加载根目录
TiXMLElement*fileArgsElement=rootElement->FirstChildEle
ment();
//获取根目录下的第一个节点
if(fileArgsElement)
{TiXMLElement* argElement = fileArgsElement->FirstChild
Element();
//获取该节点的第一个子节点
while ( argElement )
{TiXMLAttribute* attributeOffileArge = argElement-
>FirstAttribute();
//获取该子节点的第一个属性
while(attributeOffileArge)
{CString m_attribute,CString m_name;//定义cstring用于存储属性值
m_attribute.Format("%s",attributeOffileArge->Value());
m_name.Format("%s",attributeOffileArge->Name());
d_arg.Attributes.insert(pair
(m_name,m_attribute));
attributeOffileArge=attributeOffileArge->Next();}
TiXMLElement* addressElement=argElement-
>FirstChildElement();
//获取该节点的第一个值
while(addressElement)
{CString m_value,m_name;//定义cstring用于存储节点值信息
m_name.Format("%s",addressElement->Value());
m_value.Format("%s",addressElement->GetText());
d_arg.Values.insert(pair
addressElement=addressElement->NextSiblingElement(); }
Attrus.push_back(d_arg);
argElement=argElement->NextSiblingElement();//指向下一个节点}}
通过上述代码可以看到,使用包含两个map
(2)多字节字符集项目中含中文符号XML的解析
使用3.1中的方法可以轻松的实现XML文档的解析,满足了多个属性信息和值信息XML的数据存储,并将节点的值和属性分开存放到各自的map中。但是如果经常使用Tingxml的话还会发现,当在多字节字符集项目中使用Tingxml时如果解析的数据中含有中文字符,最终的输出结果将并非你所愿。这是因为Tingxml目前仅直接支持解析UTF-8编码的XML,而对于多字节字符集项目, 虽然支持UTF-8, 但却不能与控件直接交互,必须经过转换,否则不能支持中文。因此要实现多字节字符集项目中含中文符号XML的解析,就必须实现字符类型从UTF-8到ANSI的转换。
本文通过使用WideCharToMultiByte和MultiByteToWideChar转换函数,结合CP_ACP代码页和CP_UTF8代码页实现字符从UTF-8到ANSI的转换。实现方法如下:
CString ConvertUtf8ToGBK(const char* str_UTF8)
{int len = MultiByteToWideChar(CP_UTF8, 0, str_UTF8, -1, NULL, 0);
wchar_t* wsGBK = new wchar_t[len+1];memset(wsGBK, 0, len*2+2);
MultiByteToWideChar(CP_UTF8, 0, str_UTF8, -1, wsGBK, len);//将UTF-8转换成Unicode
len = WideCharToMultiByte(CP_ACP, 0, wsGBK, -1, NULL, 0, NULL, NULL);
char* sGBK = new char[len+1];memset(sGBK, 0, len+1);
WideCharToMultiByte (CP_ACP, 0, wsGBK, -1, sGBK, len, NULL, NULL);//将ANSI转换成ANSI
CString str(sGBK);
return str;}
用上述方法将Tingxml解析得到的XML数据字符从UTF-8转换成ANSI编码,就可以实现在多字节字符集项目中使用Tingxml解析含有中文字符的XML了。
3 总结
Tingxml解析库是XML文档解析的重要解析库,c++和Tingxml相结合能够快速对XML文档中的数据信息进行解析。但Tingxml自身的特征决定了它在数据存储和中文字符解析方面的局限性。通过本文方法的使用,我们可以更好的运用Tingxml对XML文档进行数据解析,实现多值多属性XML数据存储和Tingxml对中文字符的解析。
参考文献:
[1]孙晓非等. XML基础教程与实验指导[M].清华大学出版社,2008.
[2]王超,郑清. C++解析XML方法的研究和实现.电脑与电信,2012.
[3]李普曼(Stanley B.Lippman)等,C++Primer中文版[M].李师贤、蒋爱军等译.第4版.北京:人民邮电出版社,2008.