This article is the first of a 3 part tutorial on serialization.
- Part 1 introduces the basics of serialization.
- Part 2 explains how to gracefully handle reading invalid data stores and support versioning.
- Part 3 describes how to serialize complex objects.
Serialization is the process of writing or reading an object to or from a persistent storage medium, such as a disk file. Serializing an object requires 3 ingredients:
- A
CFile
object representing the datafile - A
CArchive
object that provides the serialization context - The object being serialized
Step 1 - Open the datafile
To serialize an object to the file "foo.dat", open the file with the appropriate access mode. In this example, the file is opened for exclusive read/write access.
CFile* pFile = new CFile();
ASSERT (pFile != NULL);
if (!pFile->Open ("foo.dat", CFile::modeReadWrite | CFile::shareExclusive)) {
return;
}
Step 2 - Hook Up the Archive
Next, a CArchive
object is hooked up to the file. The archive provides an efficient conduit to persistent storage. Instead of directly reading and writing the file, you serialize data to and from the archive. The archive needs to know if you're going to be using it to read or write data. In this example, we'll assume we're writing data.
bool bReading = false;
CArchive* pArchive = NULL;
try
{
pFile->SeekToBegin();
UINT uMode = (bReading ? CArchive::load : CArchive::store);
pArchive = new CArchive (pFile, uMode);
ASSERT (pArchive != NULL);
}
catch (CException* pException)
{
return;
}
Step 3 - Serialize the Object
Finally, we serialize the object by calling its serialize()
method. serialize()
is just a method we made up. It has nothing to with MFC's CObject::Serialize()
. Also, you don't have to derive your object from CObject
. Our serialize()
method takes a pointer to a CArchive
and returns an integer status.
int CFoo::serialize
(CArchive* pArchive)
{
int nStatus = SUCCESS;
...
return (nStatus);
}
We'll get to the actual serialization process in a minute. Meanwhile, let's recognize a couple of important points:
- The same method
CFoo::serialize()
is used to read/write the object from/to persistent storage. CFoo::serialize()
doesn't know anything about the datafile bring accessed.
Assume CFoo
represents an employee record that contains a couple of data members.
class CFoo
{
public:
CFoo::CFoo();
virtual CFoo::~CFoo();
public:
int serialize (CArchive* pArchive);
public:
CString m_strName;
int m_nId;
};
We use CArchive
's streaming operators <<
and >>
to read/write the data members from/to the archive. CArchive
knows how to serialize simple data types like int
, float
, DWORD
, and objects like CString
. The archive also knows whether it's in read or write mode. You can query its mode by calling CArchive::IsStoring()
. CFoo
's serialization method can then be written as:
int CFoo::serialize
(CArchive* pArchive)
{
int nStatus = SUCCESS;
ASSERT (pArchive != NULL);
try
{
if (pArchive->IsStoring()) {
(*pArchive) << m_strName;
(*pArchive) << m_nId;
}
else {
(*pArchive) >> m_strName;
(*pArchive) >> m_nId;
}
}
catch (CException* pException)
{
nStatus = ERROR;
}
return (nStatus);
}
Step 4 - Clean Up
When you've finished serializing, you should clean up by closing the archive and datafile.
pArchive->Close();
delete pArchive;
pFile->Close();
delete pFile();
Conclusion
Well, there you have it - serialization in a (very small) nutshell. In Part 2, we'll see how to gracefully handle reading invalid data stores and support different versions of our object. In Part 3, we'll see how to serialize complex objects.