16.2. Memento Pattern

[ fromfile: memento.xml id: memento ]

Abstract

This section combines QMetaObjects with the SAX2 parser to show how to write a general-purpose XML encoding/decoding tool that works on QObjects with well-defined Q_PROPERTYs and children. This gives us an example that combines the MetaObject pattern with the Memento pattern. And because XML and QObjects can both represent hierarchical structures, these ideas are combined with the Composite and Abstract Factory patterns, to save and load entire trees of polymorphic objects.

The goal of this section is to come up with serializers and deserializers for many different kinds of classes, where the encoding and decoding is handled by QMetaObject-aware methods, separate from the logic of the model.

To encode and decode trees of QObjects as XML, we must define a mapping scheme. Such a mapping must capture not only the QObject's properties, types, and values, but it must also capture existing relationships between the object and its children, between each child and all of its children, and so on.

The parent-child relationships of XML elements naturally map to QObject parents and children. These relationships define a tree structure.

Figure 16.2.  CustomerList UML

CustomerList UML

You may recall that both XML and QObject already use the Composite pattern, to support a tree-like hierarchy of objects. Figure 16.2 shows Customer and CustomerList, both derived from QObject. We use the Composite pattern here to map children of QObjects to corresponding child elements in XML.

An example of the desired XML format for storing the data of a CustomerList is shown in Example 16.10.

Example 16.10. src/xml/propchildren/customerlist.xml

<object class="CustomerList" name="Customers" >

   <object class="Customer" name="Simon" >
     <property name="Name" type="QString" value="Simon" />
     <property name="Date" type="QDate" value="1963-11-22" />
     <property name="LuckyNumber" type="int" value="834" />
     <property name="State" type="QString" value="WA" />
     <property name="Zip" type="QString" value="12345" />
     <property name="FavoriteFood" type="QString" value="Donuts" />
     <property name="FavoriteDrink" type="QString" value="YooHoo"/>
   </object>


   <object class="Customer" name="Raja" >
     <property name="Name" type="QString" value="Raja" />
     <property name="Date" type="QDate" value="1969-06-15" />
     <property name="LuckyNumber" type="int" value="62" />
     <property name="State" type="QString" value="AZ" />
     <property name="Zip" type="QString" value="54321" />
     <property name="FavoriteFood" type="QString" value="Mushrooms" />
     <property name="FavoriteDrink" type="QString" value="Jolt" />
   </object>

</object>

<include src="src/xml/propchildren/customerlist.xml" href="src/xml/propchildren/customerlist.xml" id="custlist-xmloutput" mode="txt"/>


With this kind of information in an input file, it is possible to fully reconstruct not only the properties and their types, but also the tree structure of parent-child relationships between QObjects in a CustomerList.