Rogue Wave banner
Previous fileTop of DocumentContentsIndex pageNo next file
Stingray Foundation Library User's Guide

15.5 XML Framework Tutorial

This tutorial will walk you through the steps required to add XML serialization support to an existing MFC Document-View application. (A starter application XMLTutorial is available by request from support@roguewave.com.) All tutorial steps will be based on adding to and changing the code in the XMLSerTut_Base application. You can follow along and make the changes to the code as you go, or refer to the completed application provided in the same directory.

15.5.1 The starter application

XMLSerTut_Base was generated using the MFC application wizard. It is a very simple drawing program. The user can select shapes from the toolbar and drop them onto the view window

Figure 19: The XML Starter Application.

15.5.1.1 The starter application classes

CXShape—The base class for the drawing shapes. This class contains an STL vector of POINT structures.

CXTriangle, CXCircle, and CXRectangleCXShape-derived classes used to render their respective shapes.

CXDiagram— Custom class that contains an array of CXShape objects.

CDesignDoc — The application's CDocument class. The document data consists of a title and a diagram (CXDiagram object).

15.5.2 Modifying application data classes

In our starter application the CDesignDoc, CXDiagram, and CXShape classes are all serializable classes. Each object is responsible for serializing its internal data. For XML serialization, we will be using helper classes called formatters to write each object's data as XML tags. For this to succeed we need to ensure that the internal data is publicly accessible.

For example, the CXShape class member m_vecPoints is protected. To allow us to read the shape's data, we can add two accessor methods to the class.

We can also add similar logic to the CXDiagram object to give us access to the title and array of shape objects.

We do not need to add public accessors to our document class as we will not be using a formatter class for the document. However, the internal data should be either protected or have protected accessors since we will be creating a new class derived from the existing document class. Read more on this in Section 15.5.4, "XML-enabling the document class."

15.5.3 Adding SFL XML Support

15.5.3.1 stdafx.h

To link in the SFL libraries, we need to make some modifications to our project's stdafx.h (precompiled header file).

15.5.3.2 Resource includes

  1. Open up the resource includes dialog via the View | Resource includes... menu option.

  2. Add sflres.h to the read-only symbol directives.

  3. Add sfl.rc at the bottom of the compile-time directives.

    Figure 20: Resource Includes Dialog

15.5.4 XML-enabling the document class

15.5.4.1 Using the SECXMLDocAdapter_T wrapper class

The SFL library provides a very handy wrapper class, SECXMLDocAdapter_T, for adding XML serialization to your existing CDocument class. To use this template, simply create a new class that publicly inherits from the template. The template argument is your existing document class.


The sfl:: scope declaration is a typedef for the stingray::foundation namespace. You can just as easily add the directive using namespace stingray::foundation; to your header files (or stdafx.h). However, we have chosen to use the sfl:: notation to clearly document where SFL framework classes are being used.

We must provide an implementation of the pure virtual SECXMLDocAdapter_T::XMLSerialize() method. For now, we'll simply provide the boilerplate code.

We provide an override of the GetElementType() method so that the XML framework will know what to call the top-level tag in the XML document. Our resulting XML will look like this:

15.5.4.2 Modifying the base application

Now that we have a document class capable of participating in the SFL XML framework, we need to make some small modifications to the application.

In the application object's ::InitInstance() we'll add logic to initialize the OLE libraries and the SFL framework:

Change the application's CDocTemplate to use the new XML-enabled document class:

15.5.4.3 Adding menu commands

We'll edit the menu resource to add some entries for loading and saving XML files. This is not absolutely required since the framework will actually parse the file extension when you load or save your document. If the .xml extension is found, the XML framework will call your document's XMLSerialize() override. Otherwise, your base class Serialize(CArchive& ar) method will be invoked.

Figure 21: Menu Commands

15.5.4.4 Menu command handlers

To handle the menu commands, we make some MESSAGE_MAP entries in our new document class (the one we created from the template and the original document class). You can choose any id value you want for the menu commands, as they aren't predefined in the SFL headers. You do not need to write any message handlers, because they have been provided by the template base class.

15.5.5 Creating XML formatters

We added public accessors for serializable data to our application classes so that we can create XML formatters to save our objects as XML. A formatter is a simple class that implements the IXMLSerialize interface. The framework contains a base class, CXMLSerializeImp, which can be used as the base class for our formatter classes.

Our formatter has to do three things:

We'll first concentrate on the first two requirements.

15.5.5.1 The CXShape base class formatter

We will create a class hierarchy of formatters that parallels our CXShape class hierarchy. We derive a class from CXMLSerializeImp and provide an override of the XMLSerialize() method.

15.5.5.2 Implementing CXShape::XMLSerialize()

To write out the shape data we need to do the following:

  1. Write out the number of points.

  2. Loop through our POINT vector and write each point's x and y value.

This is the same logic that exists in the standard CXShape::Serialize() method. We use the SECXMLArchive::Read() and ::Write() methods to read and write XML tags. Our first call is to read or write the point count, which will be read from and written to the <PointCount> XML tag. This is the first method parameter.

To ensure that our points can be read back into the object in the correct order, we separate each point as a unique XML child element. Here we have used the format PTxxxxxx to name the XML tags, so that the first point is <PT000001>. Each PTxxxxxx will have two child elements, <XValue> and <YValue>. Even though an STL collection is zero-based, we're using a 1-based naming convention for demonstration purposes.

15.5.5.3 Creating formatters for derived CXShape classes

We've met our first two requirements for reading and writing our shape class data as XML. So how do we satisfy the third? How can we create an instance of our object from simple XML text?

The SFL framework uses a set of macros that are similar to the MFC FOUNDATION_DECLARE_SERIAL / IMPLEMENT_SERIAL macros. These SFL macros define a lookup map that determines what XML tag corresponds to your domain object classes.

To make our concrete shape classes creatable from XML, we derive three new classes that inherit from the CXShapeFMT class we just created. We're only showing one class, but the procedure is the same for all three.

Our concrete formatter class is doing two things:

  1. Mapping a specific formatter to a specific class.

  2. Describing what the class XML tag will be (via the constructor).

In our implementation (.cpp) file, we use another SFL macro to create an instance of the XML initilization information. The macros we put in the class header declare a static nested class, and we initialize this static instance.

15.5.5.4 The CXDiagram formatter

The final class for which we need a formatter is our CXDiagram class. The process of declaring the class and initializing the lookup map is the same as it is for the shape classes.

15.5.5.5 Implementation of CXDiagramFMT::XMLSerialize()

Our diagram class needs to write out its title and list of shape objects. In the standard MFC serialization we can write the list of objects with one line of code since we are using a CTypedPtrArray, which provides a Serialize() method.

The SFL framework provides a set of prebuilt formatter classes that can accomplish the same one-line serialization for MFC collection classes. This makes our implementation of XMLSerialize() quite simple.

Here we will use the SFL-provided CTypedPtrArrayFTR formatter class. This is a template class. The two template parameters are the same as the template parameters for the CTypedPtrArray declared in the CXDiagram class.

When we call the SECXMLArchive::Read() method, we pass NULL as the first argument. For the second parameter, we create an instance of the formatter inline. The first parameter to the constructor is a pointer to the diagram's array. The second parameter determines the name of the XML tag representing the collection of objects.

The resulting XML will have this structure:

15.5.6 Finishing up

Now that we have all our formatter classes written, the only item left is to add serialization code to our new document class.

Our results: We can now run our application, create a new drawing, and save it as XML. If we've done everything correctly, our XML will look like this:



Previous fileTop of DocumentContentsNo linkNo next file

Copyright © Rogue Wave Software, Inc. All Rights Reserved.

The Rogue Wave name and logo, and Stingray, are registered trademarks of Rogue Wave Software. All other trademarks are the property of their respective owners.
Provide feedback to Rogue Wave about its documentation.