TinyXML加载保存C++对象

来源:互联网 发布:php 依赖注入 编辑:程序博客网 时间:2024/06/06 19:55

TinyXML是一个非常小巧简单的XML解析库,采用DOM方式来解析XML文件。不足的是它本身不支持DTD和XSL,但普通简单的XML使用需求还是可以满足了。

TinyXML由2个头文件四个CPP文件构成。继承结构如下:

  • TiXmlBase
    • TiXmlAttribute
    • TiXmlNode
      • TiXmlComment
      • TiXmlDeclaration
      • TiXmlDocument
      • TiXmlElement
      • TiXmlText
      • TiXmlUnknown
  • TiXmlHandle
  • TiXmlVisitor
    • TiXmlPrinter

要操作XML首先需要加载XML,很简单:

TiXmlDocument doc( "demo.xml" );doc.LoadFile();

一个更加真实的用例如下所示,加载一个XML文件然后显示内容到标准输出上。

// load the named file and dump its structure to STDOUTvoid 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 printedint 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 onlyclass 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 itvoid 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 doesif (!pElem) return;m_name=pElem->Value();// save this for laterhRoot=TiXmlHandle(pElem);}// block: string table{m_messages.clear(); // trash existing tablepElem=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 listTiXmlElement* 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-ispWindowNode->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 endconst 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 STDOUTvoid 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"。记得放在最前面。这样就可以编译通过了。

原创粉丝点击