Hi!
I came across tinyxml by chance some days ago and found it far better (easy, clear) than other 'lightweight xml' available out there. I decided to use it for saving/ retrieving program parameters.
Since there was no framework to easily map objects from and to XML-files I decided to make it myself. Here it is, and I hope someone might have a use for it, too.
Oh yes, it's for STL only (sorry for non-stl users). Everything should be clear from the example 'main.cpp'.
Features:
* Fills hierarchical organized objects with data from xml with same hierarchy (and vice versa)
* Supports strings, int, double, even list of objects
* Supports attributes: string, int, double
* Can be easily extended to match your needs
Regards
Nick
====================================
vcxmlobject.h
-------------------------------------------------------------------------
// Mapping
// Object to/ from XML
//
// Nick Goetze
#ifdef TIXML_USE_STL
#include <iostream>
#include <sstream>
#include <strstream>
#include <list>
using namespace std;
#else
#error Must be built with STL
#include <stdio.h>
#endif
// class declarations
class MyOrderItem {
VC_DECLARE_XMLOBJECT
public:
double m_dWeight;
int m_iNumber;
string m_strDescription;
};
class MyOrders {
VC_DECLARE_XMLOBJECT
public:
list<MyOrderItem> m_lxOrderItem;
};
class MyCustomer {
VC_DECLARE_XMLOBJECT
public:
string m_strName;
int m_iId;
double m_dMaxFreightWeight;
MyOrders m_xOrders;
};
int main()
{
const char* demoStart =
"<?xml version=\"1.0\" standalone='no' >\n"
"<!-- My XML object test -->"
"<Customer>customer text\n"
"<!-- Here comes a customer -->\n"
"<Name>Fred Cupertino</Name>\n"
"<Id>501</Id>\n"
"<Max_Freight_Weight>999.8</Max_Freight_Weight>\n"
"<Orders>orders text\n"
"<Order_Item Number='5' Weight='1.1' Description='Nice Product'>Order Item #1</Order_Item>\n"
"<Order_Item>Order Item #2</Order_Item>\n"
"<Order_Item Number='8'></Order_Item>\n"
"</Orders>\n"
"</Customer>";
{
// Write to a file and read it back, to check file I/O.
xCustomer.VcMapFromXml( &doc ); // get data from xml
}
{
printf("\n\n--------------------------\nobject mapped to xml\n\n");
TiXmlDocument doc( "demotest.xml" );
xCustomer.VcMapToXml( &doc ); // put data to xml
doc.Print( stdout );
}
return 0;
}
====================================
tinyxml.h
-------------------------------------------------------------------------
TiXmlText* FirstChildText( ) const; // [ng] Added
...
#ifdef TIXML_USE_STL
// [ng] new stuff used to store and retrieve attributes with double and int
const char* Attribute( const std::string& name, double* d ) const { return Attribute( name.c_str(), d ); }
void SetAttribute( const std::string& name, double value )
{
StringToBuffer n( name );
if ( n.buffer )
SetAttribute (n.buffer, value);
}
const char* Attribute( const std::string& name, std::string *str ) const
{ const char *res=Attribute( name.c_str()); if( str && res) *str=res; return res; }
Hi!
I came across tinyxml by chance some days ago and found it far better (easy, clear) than other 'lightweight xml' available out there. I decided to use it for saving/ retrieving program parameters.
Since there was no framework to easily map objects from and to XML-files I decided to make it myself. Here it is, and I hope someone might have a use for it, too.
Oh yes, it's for STL only (sorry for non-stl users). Everything should be clear from the example 'main.cpp'.
Features:
* Fills hierarchical organized objects with data from xml with same hierarchy (and vice versa)
* Supports strings, int, double, even list of objects
* Supports attributes: string, int, double
* Can be easily extended to match your needs
Regards
Nick
====================================
vcxmlobject.h
-------------------------------------------------------------------------
// Mapping
// Object to/ from XML
//
// Nick Goetze
#define VC_DECLARE_XMLOBJECT \ public: \ int VcMapToXml( TiXmlNode *pNode ) { return VcMapXml( pNode, false ); } \ int VcMapFromXml( TiXmlNode *pNode ) { return VcMapXml( pNode, true ); } \ static inline const char *VcGetTag() { return m_strXmlTag; } \ private: \ int VcMapXml( TiXmlNode *pNode, bool bMapFromXml ); \ std::string m_strXmlText; \ static char *m_strXmlTag;
#define VC_IMPLEMENT_XMLOBJECT( classT, tag ) \ char * classT::m_strXmlTag = #tag;
#define VC_MAP_XMLOBJECT_BEGIN \ if( !pNode ) return -1; \ if( bMapFromXml ) { \ if( string(m_strXmlTag) != pNode->Value() ) { \ pNode = pNode->FirstChildElement( m_strXmlTag ); \ if( !pNode ) return -1; \ } \ } \ TiXmlElement xElement(m_strXmlTag); // only used for bMapFromXml == false
#define VC_MAP_XMLOBJECT_END \ if( !bMapFromXml ) \ pNode->InsertEndChild( xElement );
#define VC_MAP_XMLOBJECT_TEXT \ if( bMapFromXml ) { \ TiXmlText *pText = pNode->FirstChildText(); \ m_strXmlText = pText ? pText->Value() : ""; \ } else { \ TiXmlText xText(m_strXmlText); xElement.InsertEndChild( xText ); \ }
#define VC_MAP_XMLOBJECT_FUNDAMENTAL_FROM( tag ) \ TiXmlElement *pElement##tag = pNode->FirstChildElement( #tag ); \ TiXmlText *pText##tag = pElement##tag ? pElement##tag->FirstChildText() : NULL;
#define VC_MAP_XMLOBJECT_FUNDAMENTAL_TO( tag, value ) \ TiXmlElement xElement##tag( #tag ); \ TiXmlText xText##tag( value ); \ xElement##tag.InsertEndChild( xText##tag ); \ xElement.InsertEndChild( xElement##tag );
#define VC_MAP_XMLOBJECT_STRING( object, tag, defaultval ) \ if( bMapFromXml ) { \ VC_MAP_XMLOBJECT_FUNDAMENTAL_FROM( tag ) \ object = pText##tag ? pText##tag->Value() : defaultval; \ } else { \ VC_MAP_XMLOBJECT_FUNDAMENTAL_TO( tag, object ) \ }
#define VC_MAP_XMLOBJECT_INT( object, tag, defaultval ) \ if( bMapFromXml ) { \ VC_MAP_XMLOBJECT_FUNDAMENTAL_FROM( tag ) \ object = pText##tag ? atoi(pText##tag->Value()) : defaultval; \ } else { \ char strTmp[48]; sprintf(strTmp, "%i", object); \ VC_MAP_XMLOBJECT_FUNDAMENTAL_TO( tag, strTmp ) \ }
#define VC_MAP_XMLOBJECT_DOUBLE( object, tag, defaultval ) \ if( bMapFromXml ) { \ VC_MAP_XMLOBJECT_FUNDAMENTAL_FROM( tag ) \ object = pText##tag ? atof(pText##tag->Value()) : defaultval; \ } else { \ char strTmp[48]; sprintf(strTmp, "%lf", object); \ VC_MAP_XMLOBJECT_FUNDAMENTAL_TO( tag, strTmp ) \ }
#define VC_MAP_XMLOBJECT_ATTRIBUTE( object, tag, defaultval ) \ if( bMapFromXml ) { \ if( !pNode->ToElement() || !pNode->ToElement()->Attribute( #tag, &object ) ) \ object = defaultval; \ } else { \ xElement.SetAttribute( #tag, object ); \ }
#define VC_MAP_XMLOBJECT_OBJECT( object ) \ if( bMapFromXml ) \ object.VcMapFromXml( pNode ); \ else \ object.VcMapToXml( &xElement );
#define VC_MAP_XMLOBJECT_OBJECTLIST( object, objecttype ) \ if( bMapFromXml ) { \ object.clear(); \ for( TiXmlNode *pNode2 = pNode->FirstChildElement(objecttype::VcGetTag()); \ pNode2; pNode2=pNode2->NextSiblingElement() ) { \ objecttype xItem; \ xItem.VcMapFromXml( pNode2 ); \ object.push_back( xItem ); \ } \ } else { \ for( std::list<objecttype>::iterator it=object.begin(); \ it!=object.end(); it++ ) { \ it->VcMapToXml( &xElement ); \ } \ }
====================================
main.cpp
-------------------------------------------------------------------------
#include "tinyxml.h"
#include "vcxmlobject.h"
#ifdef TIXML_USE_STL
#include <iostream>
#include <sstream>
#include <strstream>
#include <list>
using namespace std;
#else
#error Must be built with STL
#include <stdio.h>
#endif
// class declarations
class MyOrderItem {
VC_DECLARE_XMLOBJECT
public:
double m_dWeight;
int m_iNumber;
string m_strDescription;
};
class MyOrders {
VC_DECLARE_XMLOBJECT
public:
list<MyOrderItem> m_lxOrderItem;
};
class MyCustomer {
VC_DECLARE_XMLOBJECT
public:
string m_strName;
int m_iId;
double m_dMaxFreightWeight;
MyOrders m_xOrders;
};
// class definitions
// object xml-tag
VC_IMPLEMENT_XMLOBJECT( MyOrderItem, Order_Item )
VC_IMPLEMENT_XMLOBJECT( MyOrders, Orders )
VC_IMPLEMENT_XMLOBJECT( MyCustomer, Customer )
int MyOrderItem::VcMapXml( TiXmlNode *pNode, bool bMapFromXml )
{
VC_MAP_XMLOBJECT_BEGIN
VC_MAP_XMLOBJECT_TEXT
// object tag default
VC_MAP_XMLOBJECT_ATTRIBUTE(m_dWeight, Weight, -1)
VC_MAP_XMLOBJECT_ATTRIBUTE(m_iNumber, Number, 0)
VC_MAP_XMLOBJECT_ATTRIBUTE(m_strDescription, Description, "n.a." )
VC_MAP_XMLOBJECT_END
return 0;
}
int MyOrders::VcMapXml( TiXmlNode *pNode, bool bMapFromXml )
{
VC_MAP_XMLOBJECT_BEGIN
VC_MAP_XMLOBJECT_TEXT
VC_MAP_XMLOBJECT_OBJECTLIST( m_lxOrderItem, MyOrderItem )
VC_MAP_XMLOBJECT_END
return 0;
}
int MyCustomer::VcMapXml( TiXmlNode *pNode, bool bMapFromXml )
{
VC_MAP_XMLOBJECT_BEGIN
VC_MAP_XMLOBJECT_TEXT
VC_MAP_XMLOBJECT_OBJECT( m_xOrders )
VC_MAP_XMLOBJECT_STRING( m_strName, Name, "NoName" )
VC_MAP_XMLOBJECT_INT( m_iId, Id, -1 )
VC_MAP_XMLOBJECT_DOUBLE( m_dMaxFreightWeight, Max_Freight_Weight, -1 )
VC_MAP_XMLOBJECT_END
return 0;
}
int main()
{
const char* demoStart =
"<?xml version=\"1.0\" standalone='no' >\n"
"<!-- My XML object test -->"
"<Customer>customer text\n"
"<!-- Here comes a customer -->\n"
"<Name>Fred Cupertino</Name>\n"
"<Id>501</Id>\n"
"<Max_Freight_Weight>999.8</Max_Freight_Weight>\n"
"<Orders>orders text\n"
"<Order_Item Number='5' Weight='1.1' Description='Nice Product'>Order Item #1</Order_Item>\n"
"<Order_Item>Order Item #2</Order_Item>\n"
"<Order_Item Number='8'></Order_Item>\n"
"</Orders>\n"
"</Customer>";
{
// Write to a file and read it back, to check file I/O.
TiXmlDocument doc( "demotest.xml" );
doc.Parse( demoStart );
if ( doc.Error() )
{
printf( "Error in %s: %s\n", doc.Value(), doc.ErrorDesc() );
exit( 1 );
}
doc.SaveFile();
}
MyCustomer xCustomer; // object
{
TiXmlDocument doc( "demotest.xml" );
bool loadOkay = doc.LoadFile();
if ( !loadOkay )
{
printf( "Could not load test file 'demotest.xml'. Error='%s'. Exiting.\n", doc.ErrorDesc() );
exit( 1 );
}
printf( "** Demo doc read from disk: ** \n\n" );
doc.Print( stdout );
xCustomer.VcMapFromXml( &doc ); // get data from xml
}
{
printf("\n\n--------------------------\nobject mapped to xml\n\n");
TiXmlDocument doc( "demotest.xml" );
xCustomer.VcMapToXml( &doc ); // put data to xml
doc.Print( stdout );
}
return 0;
}
====================================
tinyxml.h
-------------------------------------------------------------------------
TiXmlText* FirstChildText( ) const; // [ng] Added
...
#ifdef TIXML_USE_STL
// [ng] new stuff used to store and retrieve attributes with double and int
const char* Attribute( const std::string& name, double* d ) const { return Attribute( name.c_str(), d ); }
void SetAttribute( const std::string& name, double value )
{
StringToBuffer n( name );
if ( n.buffer )
SetAttribute (n.buffer, value);
}
const char* Attribute( const std::string& name, std::string *str ) const
{ const char *res=Attribute( name.c_str()); if( str && res) *str=res; return res; }
// end new stuff
const char* Attribute( const std::string& name ) const { return Attribute( name.c_str() ); }
const char* Attribute( const std::string& name, int* i ) const { return Attribute( name.c_str(), i ); }
...
====================================
tinyxml.cpp
-------------------------------------------------------------------------
TiXmlText* TiXmlNode::FirstChildText( ) const // [ng] Added
{
TiXmlNode* node;
for ( node = FirstChild();
node;
node = node->NextSibling() )
{
if ( node->ToText() )
return node->ToText();
}
return 0;
}
Nick,
Looks great! Going to try it out now. Man, just in time.
Thank You, Thank You, Thank You.
Ray