要写一个xml解析,解析后获得到的数据变成各个类的对象.
解析有现成的库,使用tinyxml,但是解析出来的类库如何变成各个类的对象,
例如一下这个xml,
<musics> <music id="1"> <name>黑色幽默</name> <albumName>Jay</albumName> <year>2000</year> </music> <music id="2"> <name>爱在西元前</name> <albumName>范特西</albumName> <year>2001</year> </music> <music id="3"> <name>回到过去</name> <albumName>八度空间</albumName> <year>2002</year> </music> <music id="4"> <name>东风破</name> <albumName>叶惠美</albumName> <year>2003</year> </music> <music id="5"> <name>七里香</name> <albumName>七里香</albumName> <year>2004</year> </music> <music id="6"> <name>一路向北</name> <albumName>十一月的萧邦</albumName> <year>2005</year> </music> </musics>
我们可以设计一个CMusics类,里头包含CMusic数组
class CMusic { public: CMusic(); virtual ~CMusic(); private: string m_name; string m_albumName; int m_year; };
class CMusic class CMusics { public: CMusics(); virtual ~CMusics(); vector<CMusic*> m_musics; };
最初的想法在CMusic中定义一个函数
class CMusic { public: CMusic(); virtual ~CMusic(); //第一版 void SetValue( string key, void* value ) { if ( key == "name" ) { this->m_name = (char*)value; } else if( key == "albumName" ) { this->m_albumName = (char*)value; } else if ( key == "year" ) { this->m_year = (int)value; } //比较纠结...如果有多个对象,那不是要比多次... } private: string m_name; string m_albumName; int m_year; };
改进版本
class CMusic { public: CMusic() { m_mapfunc["id"] = &CMusic::SetName; m_mapfunc["albumName"] = &CMusic::SetAlbumName; m_mapfunc["year"] = &CMusic::SetYear; } virtual ~CMusic(); //改进版 typedef void ( CMusic::*Func )( string key, string strAttribute, void* value ); typedef map<string, Func>MusicMap; void SetName( string key, string strAttribute, void* value ) { this->m_name = (char*)value; } void SetAlbumName( string key, string strAttribute, void* value ) { this->m_albumName = (char*)value; } void SetYear( string key, string strAttribute, void* value ) { this->m_year = *(int*)value; } MusicMap m_mapfunc; private: string m_name; string m_albumName; int m_year; };
接着我们要定义一个处理xml的类IXmlAtom
class IXmlAtom { public: /** 处理xml结点 */ virtual void DealXmlNode( string strNode, string strNodeAttribute, string Value) = 0; //创建字节点指针 virtual IXmlAtom* CreateItem( string key ){ return NULL; } virtual void LoadXml(char* xmlPath); virtual void ParseXml(char* strXmlStream); private: string DumpNode(TiXmlNode * pParent,TiXmlNode * pNode,IXmlAtom* pIAtom, int flag); };
把需要生成xml对象的类继承该类.
DealXmlNode的作用是用来各个子类调用上面的回调函数
CreateItem的作用是当xml中嵌套多层xml的时候.根据名字创建类(如果只有一层xml数据则不需要重载这个函数)
其中DumpNode实现的代码如下:
string IXmlAtom::DumpNode(TiXmlNode * pParent,TiXmlNode * pNode,IXmlAtom* pIAtom, int flag) { if(pNode == NULL) { return ""; } TiXmlText * pText = NULL; TiXmlNode * pChildNode = NULL; int t = pNode->Type(); if( t == TiXmlText::TINYXML_TEXT ) //节点类型是text节点 { const char* pParentValue = pParent->Value(); pText = pNode->ToText(); string text = pText->Value(); pIAtom->DealXmlNode(pParentValue,"",text.c_str()); } else if( t == TiXmlText::TINYXML_ELEMENT ) //节点类型是Element { int num = flag; const char* pNodeValue = pNode->Value(); //输出属性 TiXmlElement * pElement = pNode->ToElement(); TiXmlAttribute * pAttr = pElement->FirstAttribute(); if(pAttr != NULL) { string tmpAttrVal = ""; string tmpAttrName = ""; do { tmpAttrVal = pAttr->Value(); tmpAttrName = pAttr->Name(); pIAtom->DealXmlNode(pNodeValue,tmpAttrName,tmpAttrVal ); }while(pAttr = pAttr->Next()); } } //循环访问它的每一个元素 TiXmlNode * pTempParent = pNode; for(pChildNode=pNode->FirstChild();pChildNode!=0;pChildNode = pChildNode->NextSibling()) { const char* data=pChildNode->Value(); IXmlAtom* pXmlAtom = pIAtom->CreateItem(pChildNode->Value()); DumpNode(pTempParent,pChildNode, pXmlAtom == NULL? pIAtom : pXmlAtom,flag+1); } return ""; }
调用
int main(int argc, char* argv[]) { //printf("Hello World! "); CMusics music; music.LoadXml("C:\1.xml"); return 0; }
demo下载地址:
xml中包含多个对象的时候.写回调函数就每次都需要定义,可以使用c++函数模板来处理