Click here to Skip to main content
Licence 
First Posted 9 Aug 2005
Views 17,284
Bookmarked 22 times

Serialization: how to really get compatible with older application versions

By | 9 Aug 2005 | Article
Saving objects in older schema versions.

Introduction

Serialization is the standard way MFC proposes to save objects into files. By the use of versionable schemas, your objects can grow and be extended and the application will still be capable of reading older files with lower schema versions.

But if you want to be able to save a file in a way that it might be readable with an older application version, you run into the limits of what MFC provides...

Situation

#define BLOCK_SCHEMA 4
IMPLEMENT_SERIAL(CBlock, CObject, BLOCK_SCHEMA|VERSIONABLE_SCHEMA)

Each class you define as DECLARE_SERIAL has a static const member of the type CRuntimeClass, and the schema version you choose for your class is hard-coded into its instantiation.

Whenever you serialize an instance of your class, its CRuntimeClass member (called classCBlock in the above example) is serialized into the archive:

void CArchive::WriteClass(const CRuntimeClass* pClassRef)
{
    ...
    this << wNewClassTag;
    ClassRef->Store(*this);
    ..

}

The CRuntimeClass implementation finally writes the schema version into the archive:

void CRuntimeClass::Store(CArchive& ar) const
{
    WORD nLen = (WORD)lstrlenA(m_lpszClassName);
    ar << (WORD)m_wSchema << nLen;
    ar.Write(m_lpszClassName, nLen*sizeof(char));

}

Now you say: "Aha, I know what he's trying to do…". Exactly. We have to find a way to change the schema version used when writing the file, in order to create a file that is compatible with an older application. But then we have to change the serialize methods of our objects as well. This is actually easier than you think…

Solution

Let's start with the Serialize method. It consists normally of two parts, the Storing part and the Loading part. The Storing part usually reads the schema version on the first line and then loads the object's members considering the schema:

void CBlock::Serialize(CArchive& ar)

{
    if (ar.IsStoring())
    {
    …

    }
    else
    {
        UINT nSchema = ar.GetObjectSchema();

        ar >> m_iType;
        ar >> m_iLines;

        if (nSchema > 3) 
            ar >> m_strDisplayName;
    }

}

Now we have to prepare a similar code for the Storing part, which takes the schema version into consideration. We know that the schema is stored in a member of the CRuntimeClass. Fortunately this member is public, so we can simply get its value (masked with VERSIONABLE_SCHEMA):

void CBlock::Serialize(CArchive& ar)

{
    if (ar.IsStoring())
    {
        UINT nSchema = classCBlock.m_wSchema & ~VERSIONABLE_SCHEMA;

        ar << m_iType;
        ar << m_iLines;

        if (nSchema > 3)
            ar << m_strDisplayName;
    }
    else
    {
        UINT nSchema = ar. GetObjectSchema();

        ar >> m_iType;
        ar >> m_iLines;

        if (nSchema > 3) 
            ar >> m_strDisplayName;
    }

}

Isn't this simple? And as you see, the Storing and Loading parts are symmetric, so it's very easy from now on to maintain this code for further schema version changes.

But now comes the more tricky part. We have to tell the CRuntimeClass that it should use a version number which is different to the one hard-coded. Since the member classCBlock is static const, we have to cast it in order to change its members. To make it even easier for me, I defined a macro which does the whole thing for me:

#define SETSCHEMA(name, schema) \
    ((CRuntimeClass*)&name::class##name)->m_wSchema = 
                                 schema | VERSIONABLE_SCHEMA

The code to change the version can now look something like this:

voidPrepareObjectSchema(int nVersion)

{
    switch (nVersion)
    {
        case VERSION3: 
        {
            SETSCHEMA(CBlock, 3);
            SETSCHEMA(CLinkedBlock, 1);
            SETSCHEMA(CParameter, 3);
            break;
        }
    case VERSION4:
    default:
        {
            SETSCHEMA(CBlock, BLOCK_SCHEMA);
            SETSCHEMA(CLinkedBlock, LINKEDBLOCK_SCHEMA);
            SETSCHEMA(CParameter, PARAMETER_SCHEMA);
            break;
        }
    }

}

Attention

It is very important that you change the schema versions back to the original values once your file is saved.

Enjoy…

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

Danoo

Software Developer (Senior)

Switzerland Switzerland

Member

4 years apprenticeship as electronics engineer
 
3.5 years studying at the Institute of Technology in Oensingen, Switzerland, graded as sofware engineer in computer science and telecommunications
 
7 years working as software developper in the machine industry (tool grinding)
 
Now working in a company that builds simulators for tanks.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board. (secure sign-in)
 
Search this forum  
 FAQ
    Noise  Layout  Per page   
  Refresh
GeneralTypo PinmemberYoSilver10:59 10 Aug '05  
GeneralAlso see... PinmemberRavi Bhavnani6:36 10 Aug '05  

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Mobile
Web02 | 2.5.120517.1 | Last Updated 10 Aug 2005
Article Copyright 2005 by Danoo
Everything else Copyright © CodeProject, 1999-2012
Terms of Use
Layout: fixed | fluid