libQtSerialization: Main Page

logo.png

Summary

-- libQtSerialization

Forward and Backward Serialization
The libQtSerialization organization
Communication between objects while reading
FAQ
Changes between versions

-- libQtSerialization copyright and license

libQtSerialization

Forward and Backward Serialization

The Qt library offers a QDataStream object for serialization. It works for writing and reading hard coded, non-changing data. In our world, however, it is rare that data stays static between versions. Actually, it is quite the opposite. It is totally unlikely that between two versions of a software that the serialized data be compatible between both versions.

This library was created to have a relatively easy way (compared to QDataStream, it should be as easy) to serialize data to a stream and re-read it from that stream. And when versions of the software change, the data can still be read and written and have your software work as expected (i.e. not crash, just ignore data that it doesn't understand.)

If you add new fields, then you should properly initialize them and not expect them to exist in the serialized data.

If you remove fields, then they are simply ignored on load and of course they cannot be saved since they don't exist.

If you rename a field, then you may have to do a little bit of work to handle the old name as if it were the new name (i.e. in general this means add one entry to the loader.)

Todo:
Version 0.1.0 of the library does not yet fully support forward and backward compatibility.

The libQtSerialization organization

  • Serializing
The library offers the QtSerialization::QWriter class to create serialized data from your classes. The QWriter is self contained so no other classes are required to create serialized data.
There are a set writeTag() functions one can use to write the basic type of data to the output stream. New writeTag() functions can be defined to support additional types (i.e. QPoint, QMatrix, ...)
  • Unserializing
The library offers the QtSerialization::QReader class to read serialized data from a stream and save the data in your class.
Contrary to the QWriter class, the reader is not self contained. It requires defining a set QField objects in QComposite objects. At this point, the supported fields are QFieldBasicType, QFieldString, and user defined QFieldTag.
The user defined tags are particularly useful when you have a class with either pre-defined sub-classes or arrays of sub-classes.
It is quite often that your software will need to either read or write serialized data, but not both. Having the reader and writer separate allows you to save on time and memory by creating only one or the other.
Another important aspect of this implementation: it attempts to limit the number of memory allocations in the process of saving or loading the data.
  • Drawback of the separate Reader/Writer
There is one drawback in having the reader and the writer separated as it is. You may save a field named "Test" and trying to reload it as "Tset". It will fail, of course. The library does not generate an error for a missing field since it is backward/forward compatible that way...
There isn't a good way to define the name of the fields in one single place in the serialization library. However, you can avoid this problem by using variables for all the names instead of entering the name each time.
 // this could be a global or a static const in your class
 namespace {
   const char *test_field = "Test";
 }

 ...
 QtSerialization::writeTag(writer, test_field, f_test_value);
 ...
 QtSerialization::QFieldInt32 f1(composite, test_field, f_test_value);
  • Exceptions
The library defines a set of exceptions that are raised whenever an error occurs. The library is actually expected to never raise an exception, however if you tweak the serialized data or read/write between versions and somehow the backword/forward compatibility is not correctly handled, then an exception is raised.
It is possible to catch all the library exceptions using the base exception: QException.

Communication between objects while reading

The writing is very straight forward. You call write() with each field that you want to save. For arrays, you simple loop through your data and call write on each item in the array. For sub-objects (pointer to another class from an object) you create a write function on that class and call it from the parent.

This is great, but the read does not benefit from such simplicity. It would be simpler if we did not have to support for dynamic array (i.e. the tree of objects and sub-objects was known, always fixed.) The QReader was written to support any number of cases with any number of parent/child relationships.

The reader makes use of a QComposite object. This object is an array of named fields. The names that are saved in the n attribute of the <v> tags in the serialized data. For example, an object may have a field named house which would look like this:

 ...<v n="house">Large house on the corner</v>...

In this case, the QComposite object would include a QFieldString named "house". When the QReader hits the tag and reads the n attribute, it then asks the QComposite object to send a signal to the corresponding QFieldString which in turn saves the data "Large house on the corner" in the field as defined in the QFieldString object.

inline_mscgraph_1

The read() function of the QField class is a pure virtual. A specific field will implement it and parse the data appropriately. The data may be a tag and text, or just text. In case of the basic types, the only data expected in a field is text. The event sample shown here presents the QField reading text between the open & close tag, converting and saving that data and then returning to the reader.

The following shows you the process when you make your object capable of reading some fields in a specialized way. As you can see, when the QField read function is called, it then forwards that signal to your object readTag() function. There you may read data, tags or text, from the QReader.

inline_mscgraph_2
Note:
If you implement a readTag() function that reads tags, remember that you still must make sure that when you return, you read the </v> tag closing your tag as expected by the QReader. Otherwise you will be out of sync. and the process will not continue to work.

FAQ

  • Do you have an example of usage?
Yes. The tests folder has a serialize.cpp file which in effect is an example of usage. It shows all the different cases that one would want to use.
The Snap! C++ snap_uri.cpp file will also include an example. This is not yet available though.
  • Why is my data saved but not reloaded?
You probably want to check the name and make sure it is the same that you use with the QWriter and the QReader. If the name doesn't match then the load will miss the saved data.
  • Why is my number not loading properly?
The library takes sign in arround. If you save a qint8 and then reload it as a quint8, then any negative number is likely to have a hard time. Similarly, if you were saving a qint32 before and now are reloading that value as a qint8, you may be losing many bits.
Floating points are saved as such so the value will not be exact unless the value represents an integer (i.e. 3.0) or a value that can be represented exactly in decimal. In a future version we will support saving floating points as decimal numbers instead. That way you will not lose any bit.

Changes between versions

  • Version 0.1.0

. First working version, although it does not fully support forward and backward compatibility.

libQtSerialization copyright and license

Copyright (c) 2012 Made to Order Software Corp.

http://snapwebsites.org/
contact@m2osw.com

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

This document is part of the Snap! Websites Project.

Copyright by Made to Order Software Corp.

Syndicate content

Snap! Websites
An Open Source CMS System in C++

Contact Us Directly