TinyXML是一个非常小巧简单的XML解析库,采用DOM方式来解析XML文件。不足的是它本身不支持DTD和XSL,但普通简单的XML使用需求还是可以满足了。
TinyXML由2个头文件四个CPP文件构成。继承结构如下:
要操作XML首先需要加载XML,很简单:
TiXmlDocument doc( "demo.xml" ); doc.LoadFile();
一个更加真实的用例如下所示,加载一个XML文件然后显示内容到标准输出上。
// load the named file and dump its structure to STDOUT void dump_to_stdout(const char* pFilename) { TiXmlDocument doc(pFilename); bool loadOkay = doc.LoadFile(); if (loadOkay) { printf("\n%s:\n", pFilename); dump_to_stdout( &doc ); // defined later
} else { printf("Failed to load file \"%s\"\n", pFilename); } }
写个简单的Main函数
int main(void) { dump_to_stdout("example1.xml"); return 0; }
读下面的文件
<?xml version="1.0" ?> <Hello>World</Hello>
输出如下
DECLARATION + ELEMENT Hello + TEXT[World]
dump_to_stdout递归遍历输出所有的XML内容,详细实现在本文的末尾。
TinyXML同样可以简单的编程生成一个上面的XML:
void build_simple_doc( ) { // Make xml: <?xml ..><Hello>World</Hello> TiXmlDocument doc; TiXmlDeclaration * decl = new TiXmlDeclaration( "1.0", "", "" ); TiXmlElement * element = new TiXmlElement( "Hello" ); TiXmlText * text = new TiXmlText( "World" ); element->LinkEndChild( text ); doc.LinkEndChild( decl ); doc.LinkEndChild( element ); doc.SaveFile( "madeByHand.xml" ); }
与其等价的写法:
void write_simple_doc2( ) { // same as write_simple_doc1 but add each node // as early as possible into the tree. TiXmlDocument doc; TiXmlDeclaration * decl = new TiXmlDeclaration( "1.0", "", "" ); doc.LinkEndChild( decl ); TiXmlElement * element = new TiXmlElement( "Hello" ); doc.LinkEndChild( element ); TiXmlText * text = new TiXmlText( "World" ); element->LinkEndChild( text ); doc.SaveFile( "madeByHand2.xml" ); }
给定一个节点,设置它的属性也很简单:
window = new TiXmlElement( "Demo" ); window->SetAttribute("name", "Circle"); window->SetAttribute("x", 5); window->SetAttribute("y", 15); window->SetDoubleAttribute("radius", 3.14159);
可以用下面的函数获得元素的所有属性:
// print all attributes of pElement. // returns the number of attributes printed int dump_attribs_to_stdout(TiXmlElement* pElement, unsigned int indent) { if ( !pElement ) return 0; TiXmlAttribute* pAttrib=pElement->FirstAttribute(); int i=0; int ival; double dval; const char* pIndent=getIndent(indent); printf("\n"); while (pAttrib) { printf( "%s%s: value=[%s]", pIndent, pAttrib->Name(), pAttrib->Value()); if (pAttrib->QueryIntValue(&ival)==TIXML_SUCCESS) printf( " int=%d", ival); if (pAttrib->QueryDoubleValue(&dval)==TIXML_SUCCESS) printf( " d=%1.1f", dval); printf( "\n" ); i++; pAttrib=pAttrib->Next(); } return i; }
我们经常通过配置文件来保存程序的一些配置信息。下面给一个实例来用XML加载和保存C++对象。
#include <string> #include <map> using namespace std; typedef std::map<std::string,std::string> MessageMap; // a basic window abstraction - demo purposes only class WindowSettings { public: int x,y,w,h; string name; WindowSettings() : x(0), y(0), w(100), h(100), name("Untitled") { } WindowSettings(int x, int y, int w, int h, const string& name) { this->x=x; this->y=y; this->w=w; this->h=h; this->name=name; } }; class ConnectionSettings { public: string ip; double timeout; }; class AppSettings { public: string m_name; MessageMap m_messages; list<WindowSettings> m_windows; ConnectionSettings m_connection; AppSettings() {} void save(const char* pFilename); void load(const char* pFilename); // just to show how to do it void setDemoValues() { m_name="MyApp"; m_messages.clear(); m_messages["Welcome"]="Welcome to "+m_name; m_messages["Farewell"]="Thank you for using "+m_name; m_windows.clear(); m_windows.push_back(WindowSettings(15,15,400,250,"Main")); m_connection.ip="Unknown"; m_connection.timeout=123.456; } };
类AppSettings提供了保存和加载配置信息的函数save和load。如下代码表示利用缺省的配置保存和加载:
int main(void) { AppSettings settings; settings.save("appsettings2.xml"); settings.load("appsettings2.xml"); return 0; }
同样的我们可以运行时修改配置:
int main(void) { // block: customise and save settings { AppSettings settings; settings.m_name="HitchHikerApp"; settings.m_messages["Welcome"]="Don't Panic"; settings.m_messages["Farewell"]="Thanks for all the fish"; settings.m_windows.push_back(WindowSettings(15,25,300,250,"BookFrame")); settings.m_connection.ip="192.168.0.77"; settings.m_connection.timeout=42.0; settings.save("appsettings2.xml"); } // block: load settings { AppSettings settings; settings.load("appsettings2.xml"); printf("%s: %s\n", settings.m_name.c_str(), settings.m_messages["Welcome"].c_str()); WindowSettings & w=settings.m_windows.front(); printf("%s: Show window '%s' at %d,%d (%d x %d)\n", settings.m_name.c_str(), w.name.c_str(), w.x, w.y, w.w, w.h); printf("%s: %s\n", settings.m_name.c_str(), settings.m_messages["Farewell"].c_str()); } return 0; }
有很多方法可以保存对象的属性信息,也就是对象的状态信息。下面的实例展示如何将对象的状态编码进XML文件。
void AppSettings::save(const char* pFilename) { TiXmlDocument doc; TiXmlElement* msg; TiXmlComment * comment; string s; TiXmlDeclaration* decl = new TiXmlDeclaration( "1.0", "", "" ); doc.LinkEndChild( decl ); TiXmlElement * root = new TiXmlElement(m_name.c_str()); doc.LinkEndChild( root ); comment = new TiXmlComment(); s=" Settings for "+m_name+" "; comment->SetValue(s.c_str()); root->LinkEndChild( comment ); // block: messages { MessageMap::iterator iter; TiXmlElement * msgs = new TiXmlElement( "Messages" ); root->LinkEndChild( msgs ); for (iter=m_messages.begin(); iter != m_messages.end(); iter++) { const string & key=(*iter).first; const string & value=(*iter).second; msg = new TiXmlElement(key.c_str()); msg->LinkEndChild( new TiXmlText(value.c_str())); msgs->LinkEndChild( msg ); } } // block: windows { TiXmlElement * windowsNode = new TiXmlElement( "Windows" ); root->LinkEndChild( windowsNode ); list<WindowSettings>::iterator iter; for (iter=m_windows.begin(); iter != m_windows.end(); iter++) { const WindowSettings& w=*iter; TiXmlElement * window; window = new TiXmlElement( "Window" ); windowsNode->LinkEndChild( window ); window->SetAttribute("name", w.name.c_str()); window->SetAttribute("x", w.x); window->SetAttribute("y", w.y); window->SetAttribute("w", w.w); window->SetAttribute("h", w.h); } } // block: connection { TiXmlElement * cxn = new TiXmlElement( "Connection" ); root->LinkEndChild( cxn ); cxn->SetAttribute("ip", m_connection.ip.c_str()); cxn->SetDoubleAttribute("timeout", m_connection.timeout); } doc.SaveFile(pFilename); }
下面的示例展示如何从XML文件中获得对象的状态信息:
void AppSettings::load(const char* pFilename) { TiXmlDocument doc(pFilename); if (!doc.LoadFile()) return; TiXmlHandle hDoc(&doc); TiXmlElement* pElem; TiXmlHandle hRoot(0); // block: name { pElem=hDoc.FirstChildElement().Element(); // should always have a valid root but handle gracefully if it does if (!pElem) return; m_name=pElem->Value(); // save this for later hRoot=TiXmlHandle(pElem); } // block: string table { m_messages.clear(); // trash existing table pElem=hRoot.FirstChild( "Messages" ).FirstChild().Element(); for( pElem; pElem; pElem=pElem->NextSiblingElement()) { const char *pKey=pElem->Value(); const char *pText=pElem->GetText(); if (pKey && pText) { m_messages[pKey]=pText; } } } // block: windows { m_windows.clear(); // trash existing list TiXmlElement* pWindowNode=hRoot.FirstChild( "Windows" ).FirstChild().Element(); for( pWindowNode; pWindowNode; pWindowNode=pWindowNode->NextSiblingElement()) { WindowSettings w; const char *pName=pWindowNode->Attribute("name"); if (pName) w.name=pName; pWindowNode->QueryIntAttribute("x", &w.x); // If this fails, original value is left as-is pWindowNode->QueryIntAttribute("y", &w.y); pWindowNode->QueryIntAttribute("w", &w.w); pWindowNode->QueryIntAttribute("hh", &w.h); m_windows.push_back(w); } } // block: connection { pElem=hRoot.FirstChild("Connection").Element(); if (pElem) { m_connection.ip=pElem->Attribute("ip"); pElem->QueryDoubleAttribute("timeout",&m_connection.timeout); } } }
函数dump_to_stdout如下:
#include "stdafx.h" #include "tinyxml.h" // ---------------------------------------------------------------------- // STDOUT dump and indenting utility functions // ---------------------------------------------------------------------- const unsigned int NUM_INDENTS_PER_SPACE=2; const char * getIndent( unsigned int numIndents ) { static const char * pINDENT=" + "; static const unsigned int LENGTH=strlen( pINDENT ); unsigned int n=numIndents*NUM_INDENTS_PER_SPACE; if ( n > LENGTH ) n = LENGTH; return &pINDENT[ LENGTH-n ]; } // same as getIndent but no "+" at the end const char * getIndentAlt( unsigned int numIndents ) { static const char * pINDENT=" "; static const unsigned int LENGTH=strlen( pINDENT ); unsigned int n=numIndents*NUM_INDENTS_PER_SPACE; if ( n > LENGTH ) n = LENGTH; return &pINDENT[ LENGTH-n ]; } int dump_attribs_to_stdout(TiXmlElement* pElement, unsigned int indent) { if ( !pElement ) return 0; TiXmlAttribute* pAttrib=pElement->FirstAttribute(); int i=0; int ival; double dval; const char* pIndent=getIndent(indent); printf("\n"); while (pAttrib) { printf( "%s%s: value=[%s]", pIndent, pAttrib->Name(), pAttrib->Value()); if (pAttrib->QueryIntValue(&ival)==TIXML_SUCCESS) printf( " int=%d", ival); if (pAttrib->QueryDoubleValue(&dval)==TIXML_SUCCESS) printf( " d=%1.1f", dval); printf( "\n" ); i++; pAttrib=pAttrib->Next(); } return i; } void dump_to_stdout( TiXmlNode* pParent, unsigned int indent = 0 ) { if ( !pParent ) return; TiXmlNode* pChild; TiXmlText* pText; int t = pParent->Type(); printf( "%s", getIndent(indent)); int num; switch ( t ) { case TiXmlNode::TINYXML_DOCUMENT: printf( "Document" ); break; case TiXmlNode::TINYXML_ELEMENT: printf( "Element [%s]", pParent->Value() ); num=dump_attribs_to_stdout(pParent->ToElement(), indent+1); switch(num) { case 0: printf( " (No attributes)"); break; case 1: printf( "%s1 attribute", getIndentAlt(indent)); break; default: printf( "%s%d attributes", getIndentAlt(indent), num); break; } break; case TiXmlNode::TINYXML_COMMENT: printf( "Comment: [%s]", pParent->Value()); break; case TiXmlNode::TINYXML_UNKNOWN: printf( "Unknown" ); break; case TiXmlNode::TINYXML_TEXT: pText = pParent->ToText(); printf( "Text: [%s]", pText->Value() ); break; case TiXmlNode::TINYXML_DECLARATION: printf( "Declaration" ); break; default: break; } printf( "\n" ); for ( pChild = pParent->FirstChild(); pChild != 0; pChild = pChild->NextSibling()) { dump_to_stdout( pChild, indent+1 ); } } // load the named file and dump its structure to STDOUT void dump_to_stdout(const char* pFilename) { TiXmlDocument doc(pFilename); bool loadOkay = doc.LoadFile(); if (loadOkay) { printf("\n%s:\n", pFilename); dump_to_stdout( &doc ); // defined later in the tutorial } else { printf("Failed to load file \"%s\"\n", pFilename); } }
如果你想在MFC中使用TinyXML,会出现这样的编译错误 fatal error C1010: unexpected end of file while looking for precompiled header。因为预编译头文件通过编译stdafx.cpp生成,可以在4个实现CPP文件中引入头#include "stdafx.h"。记得放在最前面。这样就可以编译通过了。