Environment: Visual C++ 6, ATL, MS XML
You may be already 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 in to
a binary form.
In this article I will show you how to serialize/deserialize a class
into/from XML string.
Why Serialize a object in to a XML?
With the widespread usage and
acceptance of XML as the format to exchange data over disparate systems,
serializing a object in to a 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, Java XML Parser(JAXP) etc
the XMLizing.
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 in to the schema format shown below.
Employee Schema
Uses Microsoft XDR Schema Standard
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()
- InitXMLNodeVariableMap()
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 variable as parameters to XML_NODE_VAR_MAP macro. Note that
variant data types(VT_xx) is used to specify data type.
FillAttribute()
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()
}
This method helps to initialize variables of
the employee object from the XML tree. Note that all the meat is in base class
and we just need call the base class helper method
FillAttributeHelper() here.
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 build XML tree to
represent employee object. Build the XML tree using helper macros like
START_XML, START_XML_ELEMENT etc.
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() methodXMLize() and deXMLize() are
exposed methods in employee object to serialize and deserialize. In XMLize() method, call
Save() method to build XML Tree. Once the XML tree is build, it
is easy to get the XML string by calling
IXMLDOMDocument::get_xml() method.
- deXMLize() methodIn 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 framework work if employee object
contains any 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.
That’s it. Happy XMLization.