Click here to Skip to main content
15,867,141 members
Articles / Desktop Programming / ATL

XMLize Your Class

Rate me:
Please Sign up or sign in to vote.
4.17/5 (6 votes)
2 Apr 2001 243.7K   2K   40   35
This article shows how to serialize a class into XML string.

Sample Image - XMLize.gif

Introduction

You may already be familiar with serializing C++ objects using MFC's CArchive class. Serialization is the process of storing the object properties and contained objects persistently, so that the object can be reconstructed to its previous state by loading the persistently stored data. If you use CArchive to serialize, the object will be serialized into a binary form.

In this article, I will show you how to serialize/deserialize a class into/from XML string. The object will be serialized into a text form in this case, since we are using XML.

Platform/Technology used: ATL, MSXML

Why Serialize an Object into XML?

With the widespread usage and acceptance of XML as the format to exchange data over disparate systems, serializing an object into XML has many advantages:

  • XML documents can be easily exchanged over the web
  • Can work on disparate platforms
  • XML tree can be parsed easily by using readily available XML parsers in the market such as Microsoft XML Parser

Now, let me show you how to do the XML-izing.

How it is Done?

The generic class CXMLArchive has most of the functionality to XMLize (i.e., serialize) a class and deXMLize (i.e., deserialize) a class. Just derive your class from CXMLArchive and override the pure virtual methods to provide class specific implementation. Let's take an example and see how it works.

Example

Let's take a simple employee object and see how it can be serialized into the schema format shown below.

Employee Schema

<?xml version="1.0" ?>
<Schema name="Employee-Schema" 
            xmlns="urn:schemas-microsoft-com:xml-data" 
            xmlns:dt="urn:schemas-microsoft-com:datatypes">

    <ElementType name="Id" dt:type="int"/>
    <ElementType name="FirstName" dt:type="string"/>
    <ElementType name="LastName" dt:type="string"/>
    <ElementType name="Address" dt:type="string"/>

    <ElementType name="Employee" model="closed" content="eltOnly">
        <element type="Id"/>
        <element type="FirstName"/>
        <element type="LastName"/>
        <element type="Address"/>
    </ElementType>
</Schema>

Employee Object

In employee object, do the following things to XMLize it:

In Header File

  • Include XMLArchive.h file
  • Derive employee class from CXMLArchive
  • Call XML_DECLARE_NODE_VAR_MAP(n), where n is the size of nodevarmap structure
  • Declare the following virtual functions from CXMLArchive class:
    • InitXMLNodeVariableMap()
    • FillAttribute()
    • Save()

In CPP File

Implement the Virtual Methods

InitXMLNodeVariableMap()

The purpose of this method is to maintain a dictionary of which XML node is related to which object variable, so that the XML node value can be correctly transferred to the appropriate object variable. Pass XML node, data type and pointer to the object variable as parameters to XML_NODE_VAR_MAP macro. Note that variant data type (VT_xx) is used to specify data type.

C++
void CEmployee::InitXMLNodeVariableMap()
{
    START_XML_NODE_VAR_MAP()
    //Specify xml node name, data type and 
    //the variable to hold the node value
         XML_NODE_VAR_MAP(NODE_ID, VT_I4, &m_lId)
         XML_NODE_VAR_MAP(NODE_FIRSTNAME, VT_BSTR, &m_szFirstName)
         XML_NODE_VAR_MAP(NODE_LASTNAME, VT_BSTR, &m_szLastName)
         XML_NODE_VAR_MAP(NODE_ADDRESS, VT_BSTR, &m_szAddress)
    END_XML_NODE_VAR_MAP()
}
FillAttribute()

This method helps to initialize variables of the employee object from the XML tree. Note that all the meat is in the base class and we just need call the base class helper method FillAttributeHelper() here.

C++
BOOL CEmployee::FillAttribute(MSXML::IXMLDOMNode* pXMLNode)
{
    ATLASSERT(pXMLNode);

    return FillAttributeHelper(pXMLNode, 
       XML_NODEVARMAP_STRUCT, XML_NODE_VAR_MAP_COUNT);
}
Save()

The purpose of Save() method is to build XML tree to represent employee object. Build the XML tree using helper macros like START_XML, START_XML_ELEMENT, etc.

C++
long CEmployee::Save(MSXML::IXMLDOMNode* pCurrentNode)
{
    //Empty DOM tree
    EmptyDOM();

    //Block of code below will build xml tree
    //
    XML_SET_CURRENTNODE(pCurrentNode)
    START_XML()
        START_XML_ELEMENT(NODE_EMPLOYEE)
            XML_ELEMENT_LONG(NODE_ID, m_lId)
            XML_ELEMENT_STR(NODE_FIRSTNAME, m_szFirstName)
            XML_ELEMENT_STR(NODE_LASTNAME, m_szLastName)
            XML_ELEMENT_STR(NODE_ADDRESS, m_szAddress)
        END_XML_ELEMENT()
    END_XML()
    return S_OK;
}

XMLize() Method

XMLize() and deXMLize() are exposed methods in employee object to serialize and deserialize. Call Save() method to build XML tree. Once the XML tree is built, it is easy to get the XML string by calling IXMLDOMDocument::get_xml() method.

deXMLize() Method

In deXMLize() method, just call the base class method LoadXML(), which takes care of exploring each XML node and transferring the values from XML node to appropriate object variable.

Contained Objects

So far so good. Will this work if employee object contains child objects? The answer is a resounding yes. This framework supports contained objects' XMLization too. You just have to implement ReadContainedObject() in your derived class (employee in this case). In overridden ReadContainedObject() method, just check if the node name is of the contained object and if yes, call the contained object LoadXMLNode() method and set bContainedObjectFound to TRUE.

Example of Contained Object

If the employee object has a one to one association with a Department object, let's see how this hierarchy of objects can be XMLized using this framework.

Dept Object

Dept object is a simple object with just one property, name. In Dept object, implement all the necessary virtual methods:

InitXMLNodeVariableMap()

C++
void CDept::InitXMLNodeVariableMap()
{
    START_XML_NODE_VAR_MAP()
        //Specify xml node name, data type and 
        //the variable to hold the node value
        XML_NODE_VAR_MAP("Name", VT_BSTR, &m_szName)
    END_XML_NODE_VAR_MAP()
}

FillAttribute()

C++
BOOL CDept::FillAttribute(MSXML::IXMLDOMNode* pXMLNode)
{
    ATLASSERT(pXMLNode);

    return FillAttributeHelper(pXMLNode, 
      XML_NODEVARMAP_STRUCT, XML_NODE_VAR_MAP_COUNT);
}

Save()

C++
long CDept::Save(MSXML::IXMLDOMNode* pCurrentNode)
{
    //Empty DOM tree
    EmptyDOM();

    //Block of code below will build xml tree
    //
    //MSXML::IXMLDOMNodePtr pCurrentNode = NULL;
    XML_SET_CURRENTNODE(pCurrentNode)
    START_XML()
        START_XML_ELEMENT("Dept")
            XML_ELEMENT_STR("Name", m_szName)
        END_XML_ELEMENT()
    END_XML()
    return S_OK;
}

XMLizing Dept Object

Once all the virtual methods are implemented by Dept object, the employee object just has to call the Dept object's Save() method when XMLizing and call LoadXMLNode() method when deXMLizing.

The modified employee object Save() method now looks like below:

C++
long CEmployee::Save(MSXML::IXMLDOMNode* pCurrentNode)
{
    //Empty DOM tree
    EmptyDOM();

    //Block of code below will build xml tree
    //
    //MSXML::IXMLDOMNodePtr pCurrentNode = NULL;
    XML_SET_CURRENTNODE(pCurrentNode)
    START_XML()
        START_XML_ELEMENT(NODE_EMPLOYEE)
            XML_ELEMENT_LONG(NODE_ID, m_lId)
            XML_ELEMENT_STR(NODE_FIRSTNAME, m_szFirstName)
            XML_ELEMENT_STR(NODE_LASTNAME, m_szLastName)
            XML_ELEMENT_STR(NODE_ADDRESS, m_szAddress)
            m_pDept->Save(XML_GET_CURRENTNODE); //XMLize Dept
        END_XML_ELEMENT()
    END_XML()
    return S_OK;
}

ReadContainedObject() checks if the node name is Dept and if yes, calls Dept object's LoadXMLNode() method to deXMLize Dept object.

C++
long CEmployee::ReadContainedObject(MSXML::IXMLDOMNode* pNode, 
                                      BOOL& bContainedObjectFound)
{
    ATLASSERT(pNode);
    bContainedObjectFound = FALSE;

    BSTR szTempNodeName;
    pNode->get_nodeName(&szTempNodeName);
    _bstr_t szNodeName(szTempNodeName, FALSE);

    if ( szNodeName == _bstr_t("Dept") ) //Load Dept Object
    {
        bContainedObjectFound = TRUE;
        return m_pDept->LoadXMLNode(pNode);
    }
    return S_OK;
}

You may wonder what is m_pDept and where it is created?. It is declared in employee.h as:

C++
CComObject<CDEPT>* m_pDept;

m_Dept is created in the employee constructor by the code below:

C++
HRESULT hRes = CComObject<CDEPT>::CreateInstance(&m_pDept);
ATLASSERT(SUCCEEDED(hRes));
m_pDept->AddRef();

License

This article has no explicit license attached to it, but may contain usage terms in the article text or the download files themselves. If in doubt, please contact the author via the discussion board below.

A list of licenses authors might use can be found here.


Written By
India India
Dhandapani Ammasai(Dan in short) is a software delivery manager at a top tier IT company in India.

Comments and Discussions

 
GeneralSaving XML into a file Pin
gabriel92716-Aug-05 12:08
gabriel92716-Aug-05 12:08 
GeneralHelp me please:collection of object Pin
nautilus7518-Sep-04 4:24
nautilus7518-Sep-04 4:24 
GeneralMultiple Contained Objects Pin
dpitman29-Dec-01 17:45
dpitman29-Dec-01 17:45 
GeneralIt's so funny project Pin
27-Jul-01 3:38
suss27-Jul-01 3:38 
QuestionRe: It's so funny project Pin
NickBln25-Nov-06 3:17
NickBln25-Nov-06 3:17 
GeneralXMLizing Collections Pin
Dhandapani Ammasai21-Jul-01 5:37
Dhandapani Ammasai21-Jul-01 5:37 
GeneralRe: XMLizing Collections Pin
Vler31-Aug-01 3:47
Vler31-Aug-01 3:47 
GeneralRe: XMLizing Collections Pin
hillcstephen18-Aug-04 9:06
hillcstephen18-Aug-04 9:06 
GeneralRe: XMLizing Collections Pin
alex@zoosmart.us19-Sep-05 22:27
alex@zoosmart.us19-Sep-05 22:27 
GeneralThanks..... Pin
4-Jul-01 23:49
suss4-Jul-01 23:49 
GeneralRe: Thanks..... Pin
Dhandapani Ammasai19-Jul-01 21:08
Dhandapani Ammasai19-Jul-01 21:08 
GeneralXML_ELEMENT_DOUBLE Pin
28-Jun-01 9:32
suss28-Jun-01 9:32 
QuestionWhat to do with subnodes! Pin
25-Jun-01 13:30
suss25-Jun-01 13:30 
GeneralElement attributes Pin
Robin Minto1-Jun-01 5:03
Robin Minto1-Jun-01 5:03 
GeneralRe: Element attributes Pin
1-Jun-01 5:42
suss1-Jun-01 5:42 
GeneralRe: Element attributes Pin
Robin Minto3-Jun-01 23:00
Robin Minto3-Jun-01 23:00 
I've already written a macro like that but my problem was understanding the mapping to create an object from XML in the alternative form. However, the framework mapping does seem to handle attributes without any problem when creating an object from XML. Thanks!
GeneralRe: Element attributes Pin
Dhandapani Ammasai4-Jun-01 4:36
Dhandapani Ammasai4-Jun-01 4:36 
GeneralRe: Element attributes Pin
Robin Minto4-Jun-01 5:30
Robin Minto4-Jun-01 5:30 
QuestionWill it work with w3c schema? Or Ms. XDR Schemas? Pin
24-Apr-01 6:02
suss24-Apr-01 6:02 
AnswerRe: Will it work with w3c schema? Or Ms. XDR Schemas? Pin
25-Apr-01 10:21
suss25-Apr-01 10:21 
GeneralAlan H. Pin
24-Mar-01 18:58
suss24-Mar-01 18:58 
GeneralRe: Alan H. Pin
Dhandapani Ammasai26-Mar-01 15:33
Dhandapani Ammasai26-Mar-01 15:33 
GeneralAdding another tag Pin
19-Mar-01 17:52
suss19-Mar-01 17:52 
GeneralRe: Adding another tag Pin
Dhandapani Ammasai20-Mar-01 5:08
Dhandapani Ammasai20-Mar-01 5:08 
Generalmsxml 10 slower than a class based solution Pin
mark edwards6-Mar-01 3:41
mark edwards6-Mar-01 3:41 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.