XML Property Bag

Persist and Restore your data in a manageable, reusable and extensible way with XML

XML Property Bag

This ActiveX DLL is derived from an excellent original XML PropertyBag sample by Aaron Anderson (Aaron.Anderson@mail.farmcreditbank.com). It allows you to persist and recreate your own objects directly to XML based-files using a simple interface closely related to VB's PropertyBag object. By implementing one interface in your class you will gain the ablility to save state and restore again for any number of objects to and from a single XML file. Full support for binary data means you can store blobs in your XML file too.

About XML

The advantages of using XML as a file format for storage in your application cannot be understated (although they probably have by now). XML is plain text. You can read and write XML files by hand (although you might find that a little tedious.) XML is also self-describing. Just looking at an XML file gives you a good idea what it does and it's generally easy to tell how to read the file. On top of that, XML parsers offer three unique features not often seen before the success of the HTTP protocol: they're almost always free, they have a standardised API and they're available for almost any useful computer you can think of.

Anyway, I'm sure you don't need me to tell you that XML is a good idea. Visit Microsoft's XML site for some more ideas and examples!

The XMLPropertyBag

The XMLPropertyBag object provided with this article comes in two parts: an implementable interface IXMLPropertyBag and a creatable class XMLPropertyBag. To take advantage of the XML Property Bag features, your class should implement the IXMLPropertyBag interface. Doing this effectively adds ReadProperties and WriteProperties events to your object, passing an XMLPropertyBag in as a parameter. The only difference between these events and ones in VB's PropertyBag implementations is that they are prefixed with IXMLPropertyBag_ rather than the base object type name (e.g. UserControl_ ).

Once you have implemented this interface, to provide XML saving and restoring of values can be done in two steps: firstly you write the code in the ReadProperties and WriteProperties methods, and then you write code in the object's owner to initialise reading and writing of values.

Reading and Writing properties is done in almost exactly the same way as with VB's PropertyBag object. The ReadProperty method of the XMLPropertyBag allows you to read the value of a named variant in the property bag and the WriteProperty method does the opposite. The following types of variant can be read and written:

  • Date
    Dates are stored as strings, so you may wish to convert to a fixed string representation if your XML file will be used across locales. For example, mm/dd/yyyy and dd/mm/yyyy are the US and UK representations of the same date and cannot be interchanged.
  • Number
    Numbers are stored as strings, so be careful with numbers containing decimal points if you expect your XML file to travel between locales. For example, the number 10,6 is valid on a machine with French locale settings but not on a UK machine.
  • String
    note you can also store strings using byte arrays instead if you want to be sure to save String data in a completely platform independent way. The XMLPropertyBag will automatically mark up String sections as XML CDATA if it sees any XML tokens in the string.
  • Byte Array
    Byte arrays are stored as Base64 encoded data so are guaranteed to be transferred unchanged across any system. The only disadvantage to this is that Base64 #encoding requires 4 bytes in the XML file for every 3 bytes passed in, rounded up to the nearest 4 bytes.
  • An object which Implements IXMLPropertyBag
    To do this, you pass in the object as the DefaultValue member of the ReadProperties method. There is no default ability for these objects.

The XMLPropertyBag code will attempt to read/write all other types as a string, but if there is no suitable format available you will get an "Invalid Variant Type" error.

Having implemented these methods, you can then save and restore objects into XML. In the object's owner, create a new instance of the XMLPropertyBag object. If you are saving the object's state, then call the SaveState method on the object, passing in the object to save as the first parameter. You can call SaveState on as many objects as you wish - they will be added to the same XML Document. The optional sName parameter allows you to distinguish between multiple objects of the same type in the same XML file. When complete, you can then get the XML rendering of the objects from the Contents property, which you can then save to whatever storage you are persisting the content to.

To restore objects from the XML, set the Contents property to the saved XML. Then you can call RestoreState, passing in each object you wish to restore (and optionally the name, if you used that).

Quick Start

This discussion demonstrates how you can add persistance to a simple class called in your application. First, assume the class is called cName and has the following definition:

   Option Explicit

   Private m_sFirstName As String
   Private m_sLastName As String

   Public Property Get FirstName() As String
      FirstName = m_sFirstName
   End Property
   Public Property Let FirstName(ByVal sName As String)
      m_sFirstName = sName
   End Property

   Public Property Get LastName() As String
      FirstName = m_sFirstName
   End Property
   Public Property Let LastName(ByVal sName As String)
      m_sLastName = sName
   End Property

The first step is to implement the IXMLPropertyBag interface. Add this code to the class:

   Implements IXMLPropertyBag

      ...

   Private Sub IXMLPropertyBag_ReadProperties(ByVal PropertyBag As vbalXMLPBag.XMLPropertyBag)
   '
   End Sub

   Private Sub IXMLPropertyBag_WriteProperties(ByVal PropertyBag As vbalXMLPBag.XMLPropertyBag)
   '
   End Sub

Now you can fill in your ReadProperties and WriteProperties implementations:

   Private Sub IXMLPropertyBag_ReadProperties(ByVal PropertyBag As vbalXMLPBag.XMLPropertyBag)
      '
      FirstName = PropertyBag.ReadProperty("FirstName", "")
      LastName = PropertyBag.ReadProperty("LastName", "")

   End Sub

   Private Sub IXMLPropertyBag_WriteProperties(ByVal PropertyBag As vbalXMLPBag.XMLPropertyBag)
      '
      PropertyBag.WriteProperty "FirstName", FirstName, ""
      PropertyBag.WriteProperty "LastName", LastName, ""

   End Sub

Once that is done, you can now save and restore cName objects at will. For example, this code:

   Private m_cN As New cName

   Public Function SaveName() As String

      ' Create an instance of the XML Property bag object:
      Dim oP As New XMLPropertyBag

      ' Save cName into the property bag:
      oP.SaveState m_cN

      ' Now the content is saved in the return value:
      SaveName = oP.Contents

   End Sub

   Public Sub RestoreName(ByVal sXML As String)

      ' Create an instance of the XML Property bag object:
      Dim oP As New XMLPropertyBag

      ' Load the XML into it:
      oP.Contents = sXML

      ' Restore the object:
      oP.RestoreState m_cN

   End Sub

The download code includes a sample demonstrating how to store a Person object which in turn holds a collection of Address objects.