Menu

Mapping XML to/ from C++ Object (STL only)

2003-03-13
2003-04-08
  • Nick Götze

    Nick Götze - 2003-03-13

    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=\&quot;1.0\&quot;  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;
    }

     
    • Anonymous

      Anonymous - 2003-04-08

      Nick,

        Looks great! Going to try it out now. Man, just in time.

         Thank You, Thank You, Thank You.

      Ray

       

Log in to post a comment.

Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.