Stingray® Foundation : Chapter 15 XML Serialization Architecture : Architecture Classes
Architecture Classes
 
The XML Document Adapter class
The Stingray XML serialization architecture has been designed to emulate, as closely as possible, MFC's serialization mechanism (based on CArchive and CDocument). Enabling the seamless transitioning of a standard document-view type application into the Stingray XML framework was among the architecture designers’ chief priorities.
The document adapter, SECXMLDocAdapter_T, is a template class that multiply inherits from your document's base class and IXMLSerialize. The document adapter is a core component of this architecture; it serves as a bridge between the standard MFC serialization mechanism and our XML archiving architecture. SECXMLDocAdapter_T overrides certain CDocument virtuals and creates the plumbing required for the XML serialization—such as creating the XML archive, and saving and opening .xml documents. All of this is completely transparent to the application. The developer is expected to provide only the implementation for the IXMLSerialize::XMLSerialize() override in the document.
The application's document class derives from the SECXMLDocAdapter_T class and provides the base document, either CDocument or its derivative, as a template parameter. As with ATL, because SECXMLDocAdapter_T is a template class and the CDocument-based template parameter is its base, users can conveniently retain existing custom document hierarchies—without the need for complex workarounds such as multiple-inheritance, abstraction models, etc. The application's document is hooked into the XML serialization framework. Attempting to open or save an .xml file will automatically invoke the XML serialization routine, the IXMLSerialize::XMLSerialize() override, in the document class.
If necessary, separate menu entries can be provided for the XML file open/save commands. These can be incorporated into the framework using existing command handlers in the document adapter base class. The message map entries needed for hooking the menu commands into the XML framework are shown below:
 
BEGIN_MESSAGE_MAP(CChartAppDoc, CGraphDoc)
//{{AFX_MSG_MAP(CChartAppDoc)
ON_COMMAND(ID_FILE_OPENXML, OnSECFileOpenXML)
ON_COMMAND(ID_FILE_SAVEXML, OnSECFileSaveXML)
ON_COMMAND(ID_FILE_SAVEXMLAS, OnSECFileSaveXMLAs)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
SECXMLArchive
SECXMLArchive is the CArchive equivalent of MFC's binary serialization architecture. However, unlike CArchive, SECXMLArchive is just an interface; the actual implementation is provided in the SECXMLDOMArchive-derived class. SECXMLDOMArchive uses the XML DOM interfaces specified in the Microsoft XML SDK to interact with XML documents.
An SECXMLDOMArchive instance gets created and gets associated with an .xml file by the SECXMLDocAdapter_T class. However, you would only deal with the base SECXMLArchive interface in your application. You can also create and initialize SECXMLDOMArchive yourself, if you choose not to use the document adapter class in your application. Important interfaces in SECXMLArchive are discussed in the following sections. These include:
Attributes
Insertion operations
Extraction operations
Serialize variant
Hierarchical nesting support
Attributes
 
BOOL IsLoading();
BOOL IsStoring();
These public functions let you know whether the archive is in Storing or Loading mode.
Insertion Operations
 
SECXMLArchive& Write(LPCTSTR tagName, long lVal);
This is one of the Write() overrides that allow you to insert primitive data types in your application as a child element node at the current node with the specified tagName in the XML document hierarchy. There are similar overrides for other primitive data types and CString.
It is almost always the case that you will have other non-primitive types (either custom classes or MFC classes) in your application. If you do, then you can associate such classes with an implementation of the IXMLSerialize interface—this implementation we call a formatter—and pass it on to the archive class using the following override:
 
SECXMLArchive& Write(LPCTSTR contextTagName, IXMLSerialize* pFormatter);
Take a look at the IXMLSerialize interface and formatters discussion in “XML Formatters” for more information.
Extraction Operations
The following Read() overrides closely reflect the Write() operations shown in “Insertion Operations” above.
 
// A Read override to read a string
BOOL Read(LPCTSTR tagName, LPTSTR& lpBuff, UINT& nLen,
BOOL bAssertOnFailure = FALSE, BOOL bTruncateOnOverflow = FALSE);
// Special Read override for reading child elements via their own formatters.
BOOL Read(LPCTSTR tagName, IXMLSerialize* pFormatter,
BOOL bAssertOnFailure = FALSE);
The return value indicates whether the specified tagName was found. If an element node with tagName was not found as a child of the current node, then the supplied data type is left uninitialized and a FALSE is returned. However, if you call the function with bAssertOnFailure set to TRUE (usually for critical elements) then Read() will ASSERT if the specified tagName is not found.
Serialize Variant
If you can make a single call into the SECXMLArchive— regardless of the serialization context (either loading or storing)— it makes your serialization code look simpler. That is just what the Serialize() overloads provide.
 
// A Serialize override to serialize a string
BOOL Serialize(LPCTSTR tagName, LPTSTR& lpBuff, UINT& nLen,
BOOL bAssertOnFailure = FALSE,
BOOL bTruncateOnOverflow = FALSE);
The Serialize() function will in turn call Read() or Write()— based on the archive's current context.
Hierarchical nesting support
While serializing, at any layer you could create parent element nodes and insert your data structures as child elements to that parent node, using the following functions:
 
BOOL OpenElement(LPCTSTR tagName, BOOL bAssertOnFailure = FALSE);
void CloseElement(LPCTSTR tagName);
For example, consider the following code:
 
ar.OpenElement("Brush");
ar.Write("Color", m_lColor);
ar.Write("Thickness", m_nThickness);
ar.CloseElement("Brush");
The above code will result in an XML segment as shown below:
 
<Brush>
<Color>
255
</Color>
<Thickness>
10
</Thickness>
</Brush>
IXMLSerialize
You should have at least one implementation of the IXMLSerialize interface in your application, if you want to participate in the XML serialization framework. This one implementation should be associated with the class (usually CDocument) that contains the data in your application. This association is automatically set up for you when you derive your document class from the SECXMLDocAdapter_T base, which in turn multiply inherits IXMLSerialize. You should then provide an implementation of the XMLSerialize virtual in your document class.
You could serialize all your application data from this single XMLSerialize override of your single IXMLSerialize implementation or you could provide other IXMLSerialize implementations for other non-primitive data types in your application and use the corresponding Read()/Write()/Serialize() override to serialize those objects from within any other XMLSerialize override.
Every IXMLSerialize implementation should also be associated with a tagName, which will be the name for the corresponding element node in the resultant XML document.
For example, a Write() call with the following syntax will produce an XML document segment as shown below.
 
ar.Write("AchildElement", MyObjectFTR(MyObject));
MyObjectFTR() is an implementation of IXMLSerialize with a tagName of say "MyObject." This will result in the following XML segment under the current node:
 
<AchildElement>
<MyObject>
...
<!-- Other element nodes serialized from inside MyObjectFTR's -->
<!-- XMLSerialize override -->
</MyObject>
</AchildElement>
We will be referring to an implementation of IXMLSerialize associated to an object as a formatter for that object. “XML Formatters” will discuss formatters.