Object serialization using XTI and XML
This page demonstrates how an object with properties can be saved to an XML file, and later reloaded. The code below makes use of the XTI features of wxWidgets.
Note: for the code below to work, you must build your wx library with wxUSE_EXTENDED_RTTI set to 1 (in setup.h). |
Note: Warning : XTI is experimental and as such you might not want to rely on it at this time for production purposes |
person.h
Here is the complete code of a simple person class that has an Age property:
#ifndef _WX_PERSON_H_
#define _WX_PERSON_H_
#include "wx/object.h"
class wxPerson : public wxObject
{
public:
wxPerson( const int age) { Create(age); }
virtual ~wxPerson() {}
void Create(const int age = 0) { m_Age = age; }
private:
wxIMPLEMENT_PROPERTY(Age, int)
DECLARE_DYNAMIC_CLASS(wxPerson)
};
#endif // _WX_PERSON_H_
Notes
- Notice that your class MUST have a Create method.
wxIMPLEMENT_PROPERTY
wxIMPLEMENT_PROPERTY is a helper implementation for the simple properties whose setters and getters involve nothing but the obvious. Notice that in the code above, this macro:
wxIMPLEMENT_PROPERTY(Age, int)
will be replaced by the preprocessor with this code:
public:
void SetAge(int const p) { m_Age = p; }
int const GetAge() const { return m_Age; }
private:
int m_Age;
You might wish NOT to use wxIMPLEMENT_PROPERTY if you want to implement more complex setters and getters.
person.cpp
Since both the constructor and the Create methods are implemented in the header file, the implementation code looks like so:
#include "person.h"
#if wxUSE_EXTENDED_RTTI
IMPLEMENT_DYNAMIC_CLASS_XTI(wxPerson, wxObject, "person.h")
wxBEGIN_PROPERTIES_TABLE(wxPerson)
wxPROPERTY( Age, int, SetAge, GetAge, 3, 0 /*flags*/ , wxT("Helpstring") , wxT("group") )
wxEND_PROPERTIES_TABLE()
wxBEGIN_HANDLERS_TABLE(wxPerson)
wxEND_HANDLERS_TABLE()
wxCONSTRUCTOR_1(wxPerson, int, Age)
#else
IMPLEMENT_DYNAMIC_CLASS(wxPerson, wxObject)
#endif
Notes
- You must include the wxBEGIN_HANDLERS_TABLE block, even if empty.
- The property name ('Age' in the example above) must be identical for both the wxPROPERTY and wxCONSTRUCTOR_X macros.
- Use wxCONSTRUCTOR_2 for constructors involving 2 parameters, wxCONSTRUCTOR_3 for 3 parameters, and so on...
Saving the object
To stream the object and its properties to an XML file, the following code can be used:
wxPerson *person = new wxPerson(8);
wxXmlDocument xml;
wxXmlNode *root;
root = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("root"), wxT(""));
xml.SetRoot(root);
wxXmlWriter writer(root);
wxPersister persister;
wxxVariantArray metadata;
writer.WriteObject( person, person->GetClassInfo() , &persister , wxString(wxT("person")), metadata );
xml.Save(wxT("myXML.xml"));
This will create an XML file that looks like so:
<?xml version="1.0" encoding="utf-8"?>
<root>
<entry name="person">
<object class="wxPerson" id="0">
<prop name="Age">8</prop>
</object>
</entry>
</root>
Loading the Object
The following code creates and loads the object from the XML file and debug-output the person's age:
wxXmlDocument xml;
wxXmlNode *root;
wxRuntimeDepersister Callbacks;
xml.Load(wxT("myXML.xml"));
root = xml.GetRoot();
if (root->GetName() != wxT("root"))
{
assert(!"Bad XML file");
}
wxXmlReader Reader(root);
int obj = Reader.ReadObject( wxString(wxT("person")) , &Callbacks );
wxPerson *person = (wxPerson*)Callbacks.GetObject(obj);
wxLogDebug(wxT("%d"), person->GetAge());