Stingray® Foundation : Chapter 15 XML Serialization Architecture : XML Formatters
XML Formatters
A formatter of an object is any implementation of IXMLSerialize that is meant to XML serialize that object. Most of our formatters also can (optionally) dynamically create an instance of the associated class type while loading an XML document.
Built-in Formatters
You can use our built-in formatters for MFC collection classes and GDI objects in your own applications.
The XML Formatter Factory
The XML Formatter Factory is a behind-the-scenes global singleton used by the Stingray XML persistence framework to mimic the CObject-based polymorphic serialization provided by MFC. The factory object is used by the formatters, provided with the SEC XML framework, for the MFC collection classes such as CObArray and CObList that have native support for serializing CObject-based classes. The Formatter Factory is implemented by the SECXMLFormatterFactory class; building the Stingray Foundation Library with the XML Serialization option selected in the SFL Build Wizard will define a global instance of the factory object.
Formatters for the MFC CObject-derived classes that are a part of the serialization routine have to be first registered with the factory before any of the XML persistence functions can be invoked. During an XML file save operation, if a collection of CObject-derived classes is encountered, the collection formatter will:
1. query the global factory instance for element type information— identifying the class along with the associated formatter,
2. write the retrieved type information under the TYPE tag of the element node for the particular instance,
and then...
3. invoke the serialization routine on the class formatter.
This sequence is reversed during a file open operation. The collection formatter first reads the tag descriptor for an element and then queries the factory for the formatter associated with this type. Failing to register formatters with the factory will preclude this type look-up, resulting in a breakdown of the serialization attempt.
Formatter registration is performed using the set of XMLFORMATTERMAP() macros; the macros can be placed within the formatters themselves or can be used within a higher-level class, such as the document, that possesses knowledge of the persisted types and performs a collective registration of all the formatters.
Usage of the formatter map macros for registering formatter classes is shown below:
 
// Declaration of the XML formatter class for the SRGraphStyle
// CObject based class
class SRGraphStyleFTR : public IXMLSerialize
{
BEGIN_SEC_XMLFORMATTERMAP(SRGraphStyleFTR)
XMLFORMATTERMAP_ADDENTRY(SRGraphStyle, SRGraphStyleFTR)
END_SEC_XMLFORMATTERMAP()
virtual void XMLSerialize(SECXMLArchive& ar);
};
 
// Implementation of SRGraphStyleFTR
DEFINE_SEC_XMLFORMATTERMAP(SRGraphStyleFTR)
 
void SRGraphStyleFTR::XMLSerialize(SECXMLArchive& ar)
{
if(ar.IsStoring())
{
}
else
{
}
}
NOTE >> The XMLFORMATTERMAP series of macros aids only in the serialization of MFC collection classes—such as CObList and CObArray—that natively support persistence of CObject pointers. XMLFORMATTERMAP is not a replacement for the SEC_XML_DYNCREATE_OBJECT macro, which provides a more generic creation mechanism.
Once the formatters and the accompanying registration macros are in place, the Formatter Factory has to be initialized. This can be done as part of the application's start-up code in the CWinApp::InitInstance() override.
 
// Initialize the XML formatter factory
SECXMLInitFTRFactory();
Collection Class Formatters
The Stingray XML framework includes formatters for the commonly used MFC collection classes (arrays, lists, maps, etc.). The collection class formatters observe all the semantics of any other XML formatter and, therefore, their usage is virtually identical. These formatters implement the IXMLSerialize interface and accept a reference to a pointer as a constructor argument. While serializing a collection class, within the XMLSerialize() override, create an equivalent formatter for the collection and pass this to the SECXMLArchive::Read() or Write() function. The following code snippet demonstrates using some of the simpler collection classes,
 
// m_dwArray is a CDWordArray
CDWordArray* pDWArray = &m_dwArray;
ar.Write("MyDWords", CDWordArrayFTR(pDWArray));
 
// m_strArray is a CStringArray
CStringArray* pStrArray = &m_strArray;
ar.Write("MyStrings", CStringArrayFTR(&pStrArray));
 
// m_strList is a CStringList
CStringList* pStringList = &m_strList;
ar.Read("MyStrings", CStringListFTR(pStringList));
Providing the formatters for collections of native types is straightforward. More complex collections— such as CPtrArray, CObArray, CPtrList, CObList, CList (template) and typed collections— require formatters to be provided for the serialized objects as well.
You will have noticed, undoubtedly, that the Stingray XML framework’s built-in serialization support provided for the pointer collection classes is over and above what is provided in MFC. A restriction here, however, is that the serialization support for pointers (as well as for the simple template-based collections) is limited to one specific type. The polymorphic serialization behavior exhibited by the CObject collections and their equivalent formatters is not available. The collection formatter is updated directly by the underlying class formatter during template instantiation. Usage of the pointer and simple template-based collections is shown in the code below,
 
// m_list is of type CList<CPoint, CPoint&>
CPointList* pList = &m_list;
ar.Read("MyCList", CListFTR<CPoint, CPoint&, CPointFTR>(pList));
 
// m_ptrList is of type CPtrList
CPtrList* ptrList = &m_ptrList;
ar.Read("MyCPtrList", CPtrListFTR<CSimpleObj, CSimpleObjFTR>(ptrList));
The XML formatter specification for serializable CObject-based classes is done through the Formatter Factory. As mentioned in “The XML Formatter Factory”, it is important that the registration macros be an integral part of either the formatter or an encompassing class. Once all formatters have been registered with the Formatter Factory, using the CObject-based collection classes is straightforward and much like using the native type collections. The following excerpt demonstrates this:
 
// m_obList is a CObList
CObList* pObList = &m_obList;
ar.Read("MyObList", CObListFTR(pObList));
 
// m_typedList is a CTypedPtrList<CObList, CMyObject*>
CMyTypedList* pTypedList = &m_typedList;
ar.Read("MyTypedList", CTypedPtrListFTR<CObList, CMyObject*>(pTypedList));
Other MFC types Formatters
CBrushFTR, CPenFTR, CFontFTR, CRectFTR and CPointFTR are other built-in formatters for some MFC classes. Their names imply their functions.
Creating Custom Formatters
To create a custom formatter, derive a class from IXMLSerialize or CXMLSerializeImp and override XMLSerialize. Our additional recommendations include:
Take a pointer reference to the associated object type in the constructor.
Provide a way for the user to specify the element tag name in the second argument.
Enable your formatter to dynamically create the underlying object while loading using a single macro— SEC_XML_DYNCREATE_OBJECT()—as shown below:
 
class CMyObjFormatter : public CXMLSerializeImp
{
public:
CMyObjFormatter(CMyObject*& pObj, LPCTSTR strElementType = _T("MyObject"))
: CXMLSerializeImp(strElementType), m_ptrObj(pObj){};
 
virtual void XMLSerialize(SECXMLArchive& ar);
 
SEC_XML_DYNCREATE_OBJECT(CMyObject)
};
XML Serialization Support in Objective Grid and Objective Chart
Objective Grid and Objective Chart use this XML Serialization architecture to optionally serialize their data in XML format. For more information, take a look at the User’s Guide for each of these products.