Trolltech Home | Qt-interest Home | Recent Threads | All Threads | Author | Date
All threads index page 7

Qt-interest Archive, April 2007
Implementing XML serialisation - using QT4


Message 1 in thread

Hi folks,

I'm looking to do the rather common task of saving and loading my application
objects to an XML file on disk using serialisation. Hre is the solution I've
come up with: my objects inherit from a class called AbstractSerialisable
which is basically an interface/common functionality for serialisation. My
actual objects which derive from it then implement a the toXmlElement and
fromXmlElement pure virtual methods, so it's relatively easy to make all my
object consistently serialise across my app. The question I have is, is there
some functionality built into the QT4 framework that I should be using
instead of writing my own custom solution. From taking a quick look I couldn't
find anything in QT Assistant.

I've provided an example below which is the .h and .cpp files of my
AbstractSerialisable class (the one that forms the basis of the XML
serialisation) and also the implemented pure virtual methods on a class
called SequenceNumberManager (which inherits from AbstractSerialisable and is
what I want to serialise).

Cheers,
Declan

*****************************************************************************
AbstractSerialisable.h
*****************************************************************************
class AbstractSerialisable
    {

    public:
        // Number of whitespace characters to use when indenting
        // nested XML elements
        static const int INDENT_WIDTH = 2;

        // The doctype of the XML document
        static const QString DOCTYPE;

    public:
        // No constructor specified

        // Destructor
        virtual ~AbstractSerialisable();

        // Methods
        virtual QString serialise() const;
        virtual void saveToFile(const QString &filename) const;
        virtual void loadFromFile(const QString &filename);
        virtual QDomDocument toQDomDocument() const;
        virtual QDomElement toXmlElement(QDomDocument &doc) const = 0;
        virtual void fromXmlElement(const QDomElement &element) = 0;
    };

*****************************************************************************
AbstractSerialisable.cpp
*****************************************************************************
const QString AbstractSerialisable::DOCTYPE = "none";

AbstractSerialisable::~AbstractSerialisable()
{
}

/**
* Saves the class to disk. This method calls the
* virtual method toQDomDocument() which the class
* which inherits from AbstractSerialisable will
* override in order to control serialisation
* @param filename Name of the file to save to
*/
void AbstractSerialisable::saveToFile(QString const &filename) const
{
    QDomDocument doc = this->toQDomDocument();

    QFile file(filename);
    if (file.open(QFile::WriteOnly | QFile::Truncate))
    {
        QTextStream fileStream(&file);
        doc.save(fileStream, AbstractSerialisable::INDENT_WIDTH);
        file.close();
    }
    else
    {
        QMessageBox::information(0, "Error", "Error opening file");
    }
}


/**
* Loads the class from disk. This method calls the
* virtual method fromQDomDocument() which the class
* which inherits from AbstractSerialisable will
* override in order to control serialisation
* @param filename Name of the file to load from
*/
void AbstractSerialisable::loadFromFile(QString const &filename)
{
    QDomDocument doc(AbstractSerialisable::DOCTYPE);
    QFile file(filename);

    if (file.open(QIODevice::ReadOnly))
    {
        if (doc.setContent(&file))
        {
            // Compare the element names of all elements that are direct
children
            // of the outermost element.
            const QDomElement& diveListElement = doc.documentElement();
            this->fromXmlElement(diveListElement);

            // Use for debugging
            //ProjectHelper::displayMessage(this->serialise());
        }
        else
            file.close();
    }
    else
        file.close();
}

/**
* Returns the class as an XML string
* @return XML string representation of the class
*/
QString AbstractSerialisable::serialise() const
{
    QDomDocument doc = this->toQDomDocument();
    QString xml = doc.toString();

    return xml;
}

/**
* Returns the class as a QDomDocument
* @return QDomDocument representation of the class
*/
QDomDocument AbstractSerialisable::toQDomDocument() const
{
    QDomDocument doc(AbstractSerialisable::DOCTYPE);
    QDomElement element = this->toXmlElement(doc);
    doc.appendChild(element);

    return doc;
}


*****************************************************************************
SequenceManager.cpp (an example of an object inheriting from
AbstractSerialisable)
*****************************************************************************

QDomElement SequenceNumberManager::toXmlElement(QDomDocument &doc) const
{
    // Create top level XML element
    QDomElement seqNoManagerElement =
m_xmlSerialiser.createNodeElement(doc, "sequencenumbermanager");

   // Add some child elements
    QDomElement tempElement =
m_xmlSerialiser.createTextElement(doc, "SequenceNumber",
QVariant(this->diveSequenceNumber()).toString());
    seqNoManagerElement.appendChild(tempElement);

    tempElement = m_xmlSerialiser.createTextElement(doc, "RegionSeqNumber",
QVariant(this->regionSequenceNumber()).toString());
    seqNoManagerElement.appendChild(tempElement);

    return seqNoManagerElement;
}

void SequenceNumberManager::fromXmlElement(const QDomElement
&seqNoManagerElement)
{
    // firstChild is <sequencenumbermanager />
    QDomNode n = seqNoManagerElement.firstChild();

    while(!n.isNull())
    {
        QDomElement e = n.toElement(); // try to convert the node to an
element.
        if(!e.isNull())
        {

            if (e.tagName() == "SequenceNumber")
            {
                this->m_countrySequenceNumber = QVariant(e.text()).toInt();
            }
            else if (e.tagName() == "RegionSeqNumber")
            {
                this->m_diveSequenceNumber = QVariant(e.text()).toInt();
            }
        }
        n = n.nextSibling();
    }
}

--
 [ signature omitted ] 

Message 2 in thread

On Saturday 28 April 2007, Declan McGrath wrote:
> Hi folks,
>
> I'm looking to do the rather common task of saving and loading my
> application objects to an XML file on disk using serialisation. Hre is the
> solution I've come up with: my objects inherit from a class called
> AbstractSerialisable which is basically an interface/common functionality
> for serialisation. My actual objects which derive from it then implement a
> the toXmlElement and fromXmlElement pure virtual methods, so it's
> relatively easy to make all my object consistently serialise across my app.
> The question I have is, is there some functionality built into the QT4
> framework that I should be using instead of writing my own custom solution.
> From taking a quick look I couldn't find anything in QT Assistant.

If you have time to learn something new, you can take a look at boost ( 
www.boost.org ) with has a serialization library ( 
http://www.boost.org/libs/serialization/doc/index.html ). The learning curve 
may seem steep if you're not used to modern C++ paradigms but it's surly 
worthed. 

From what i know qt does not provide anything for serialization.


As for a custom solution, it all depends on the complexity you need. Will your 
objects have child objects that also need to be serializable? Can your number 
of serializable objects increase in time ? If yes then you need to be very 
carefull when designing your solution. A few hints:

1. in the base class for serialization only have 2 private virtual methods 
toXML and fromXML and 2 public non-virtual methods SaveToXML and LoadFromXML. 
This way in the public methods in the base class you will implement the 
general serialization logic ( node creation , child-parent relationship ... ) 
and in the private methods in the derived objects you will ONLY implement the 
saving of object state ( members, properties ) in the XML element and the 
loading of the state from the element ( in the derived classes you will not 
have to worry about node creation , addition to parent and other stuff that 
is implemented in the 2 public methods of the base class). For more info 
google on the Template Design Pattern. Having the 2 virtual function private 
may seem like a hard restriction  but it's a must. Remember that virtual 
functions are like members - make them private unless you have a really good 
reason not to ( don't remember who said this but it's so right ).

2. For the creation of the objects you may consider using a Factory pattern 
with based on the tagName of the element will create the right instance of 
the object and then call the load method from the object. A nice way of 
implementing this kind of factory can be found in Andrei Alexandrescu's 
book "modern c++ design". As an idea :

in the cpp file where the derived class is implemented 

namespace {
serializable* makeDerivedInstance() { return new Derived();  }

static bool registerDerived = 
Factory::instance().registerCreator( "derivedTag",makeDerivedInstance);
}

and on the loading of xml 

QDomNode n = d.firstChild();
 while (!n.isNull()) {
     if (n.isElement()) {
         QDomElement e = n.toElement();
 	 serializable* obj = Factory::instance().createObject(e.tagName());
	 obj->loadFromXML(e);
     }
     n = n.nextSibling();
 }


<<snip example>>


In the end i really recommend taking a look at boost::serialization with will 
not only make you interested in other parts of boost but will also "infect" 
you with the idea of modern c++ ( with is a good thing ) :) 


With hope i've made sense and was helpfull ... 

-- 
 [ signature omitted ] 

Attachment: pgp1OFjc1rxrc.pgp
Description: PGP signature


Message 3 in thread

Thanks a million for your response, Iulian. I will stick with a custom 
solution for now and take your tips on board. When I get the chance in the 
future I might get into this 'modern C++' stuff :-) Boost does sound quite 
cool indeed. Plenty of food for thought.

Cheers,
Declan

On Sunday 29 April 2007 22:15, Iulian M wrote:
> On Saturday 28 April 2007, Declan McGrath wrote:
> > Hi folks,
> >
> > I'm looking to do the rather common task of saving and loading my
> > application objects to an XML file on disk using serialisation. Hre is
> > the solution I've come up with: my objects inherit from a class called
> > AbstractSerialisable which is basically an interface/common functionality
> > for serialisation. My actual objects which derive from it then implement
> > a the toXmlElement and fromXmlElement pure virtual methods, so it's
> > relatively easy to make all my object consistently serialise across my
> > app. The question I have is, is there some functionality built into the
> > QT4 framework that I should be using instead of writing my own custom
> > solution. From taking a quick look I couldn't find anything in QT
> > Assistant.
>
> If you have time to learn something new, you can take a look at boost (
> www.boost.org ) with has a serialization library (
> http://www.boost.org/libs/serialization/doc/index.html ). The learning
> curve may seem steep if you're not used to modern C++ paradigms but it's
> surly worthed.
>
> From what i know qt does not provide anything for serialization.
>
>
> As for a custom solution, it all depends on the complexity you need. Will
> your objects have child objects that also need to be serializable? Can your
> number of serializable objects increase in time ? If yes then you need to
> be very carefull when designing your solution. A few hints:
>
> 1. in the base class for serialization only have 2 private virtual methods
> toXML and fromXML and 2 public non-virtual methods SaveToXML and
> LoadFromXML. This way in the public methods in the base class you will
> implement the general serialization logic ( node creation , child-parent
> relationship ... ) and in the private methods in the derived objects you
> will ONLY implement the saving of object state ( members, properties ) in
> the XML element and the loading of the state from the element ( in the
> derived classes you will not have to worry about node creation , addition
> to parent and other stuff that is implemented in the 2 public methods of
> the base class). For more info google on the Template Design Pattern.
> Having the 2 virtual function private may seem like a hard restriction  but
> it's a must. Remember that virtual functions are like members - make them
> private unless you have a really good reason not to ( don't remember who
> said this but it's so right ).
>
> 2. For the creation of the objects you may consider using a Factory pattern
> with based on the tagName of the element will create the right instance of
> the object and then call the load method from the object. A nice way of
> implementing this kind of factory can be found in Andrei Alexandrescu's
> book "modern c++ design". As an idea :
>
> in the cpp file where the derived class is implemented
>
> namespace {
> serializable* makeDerivedInstance() { return new Derived();  }
>
> static bool registerDerived =
> Factory::instance().registerCreator( "derivedTag",makeDerivedInstance);
> }
>
> and on the loading of xml
>
> QDomNode n = d.firstChild();
>  while (!n.isNull()) {
>      if (n.isElement()) {
>          QDomElement e = n.toElement();
>  	 serializable* obj = Factory::instance().createObject(e.tagName());
> 	 obj->loadFromXML(e);
>      }
>      n = n.nextSibling();
>  }
>
>
> <<snip example>>
>
>
> In the end i really recommend taking a look at boost::serialization with
> will not only make you interested in other parts of boost but will also
> "infect" you with the idea of modern c++ ( with is a good thing ) :)
>
>
> With hope i've made sense and was helpfull ...

--
 [ signature omitted ] 

Message 4 in thread

On 29.04.07 13:15:21, Iulian M wrote:
> On Saturday 28 April 2007, Declan McGrath wrote:
> > Hi folks,
> >
> > I'm looking to do the rather common task of saving and loading my
> > application objects to an XML file on disk using serialisation. Hre is the
> > solution I've come up with: my objects inherit from a class called
> > AbstractSerialisable which is basically an interface/common functionality
> > for serialisation. My actual objects which derive from it then implement a
> > the toXmlElement and fromXmlElement pure virtual methods, so it's
> > relatively easy to make all my object consistently serialise across my app.
> > The question I have is, is there some functionality built into the QT4
> > framework that I should be using instead of writing my own custom solution.
> > From taking a quick look I couldn't find anything in QT Assistant.
> 
> If you have time to learn something new, you can take a look at boost ( 
> www.boost.org ) with has a serialization library ( 
> http://www.boost.org/libs/serialization/doc/index.html ). The learning curve 
> may seem steep if you're not used to modern C++ paradigms but it's surly 
> worthed. 

But don't use it in debug mode, it'll create extremely large libraries
(something like 15 meg for a lib that is 1.2Meg in release mode - static
lib). And with gcc boost increases linkage times in debug mode.

Andreas

-- 
 [ signature omitted ] 

Message 5 in thread

Thanks Andreas. My little Pentium III laptop is under enough pressure already 
without needing 15meg debug libs flying around :-)

Dec

On Sunday 29 April 2007 23:09, Andreas Pakulat wrote:
> On 29.04.07 13:15:21, Iulian M wrote:
> > On Saturday 28 April 2007, Declan McGrath wrote:
> > > Hi folks,
> > >
> > > I'm looking to do the rather common task of saving and loading my
> > > application objects to an XML file on disk using serialisation. Hre is
> > > the solution I've come up with: my objects inherit from a class called
> > > AbstractSerialisable which is basically an interface/common
> > > functionality for serialisation. My actual objects which derive from it
> > > then implement a the toXmlElement and fromXmlElement pure virtual
> > > methods, so it's relatively easy to make all my object consistently
> > > serialise across my app. The question I have is, is there some
> > > functionality built into the QT4 framework that I should be using
> > > instead of writing my own custom solution. From taking a quick look I
> > > couldn't find anything in QT Assistant.
> >
> > If you have time to learn something new, you can take a look at boost (
> > www.boost.org ) with has a serialization library (
> > http://www.boost.org/libs/serialization/doc/index.html ). The learning
> > curve may seem steep if you're not used to modern C++ paradigms but it's
> > surly worthed.
>
> But don't use it in debug mode, it'll create extremely large libraries
> (something like 15 meg for a lib that is 1.2Meg in release mode - static
> lib). And with gcc boost increases linkage times in debug mode.
>
> Andreas

--
 [ signature omitted ] 

Message 6 in thread

Declan McGrath wrote:
> I'm looking to do the rather common task of saving and loading my
> application objects to an XML file on disk using serialisation. Hre is the
> solution I've come up with: my objects inherit from a class called
> AbstractSerialisable which is basically an interface/common functionality
> for serialisation. My actual objects which derive from it then implement a
> the toXmlElement and fromXmlElement pure virtual methods, so it's
> relatively easy to make all my object consistently serialise across my
> app. The question I have is, is there some functionality built into the
> QT4 framework that I should be using instead of writing my own custom
> solution. From taking a quick look I couldn't find anything in QT
> Assistant.

There is some excellent information towards achieving this goal in the
book "An Introduction to Design Patterns in C++ with Qt 4" (Anal Ezust and
Paul Ezust, Prentice Hall, ISBN 0-13-187905-7).

--
 [ signature omitted ]