Introducción a la serialización.
Por: Demian Panello.
Serialización es el proceso de leer o almacenar un objeto en un archivo.
Para serializar un objeto se requiere.
Un objeto CFile que represente el archivo.
Un Objeto CArchive que ofrece un entorno para la serialización.
El objeto a ser serializado.

Figura1
Creación de una clase que se pueda serializar:
Como el proceso de serializar permite escribir un objeto en un archivo, (también leerlo), la clase a la cual pertenece dicho objeto debe estar definida de una forma tal que permita serializarla.
Pensemos entonces en una clase como por ejemplo CEmpleado que contenga dos datos miembros, CString Apellido y float Salario. Para que un objeto de esta clase se pueda serializar, (o sea se pueda guardar en un archivo), deberemos, en principio, derivar esta clase de la clase CObject la cual es la encargada de los procesos de serialización. CObject tiene una función miembro Serialize que recibe un parámetro de tipo CArchive que lo usa para escribir y leer los datos de un objeto. Luego que uno deriva su clase de CObject, se debe sobreescribir la función miembro hererada Serialize.
Veamos:
class CEmpleado:public CObject
{
public:
DECLARE_SERIAL(CEmpleado) //esta macro es necesaria.
//Datos miembros
CString Apellido;
float Salario;
CEmpleado(); //constructor por defecto es obligatorio.
CEmpleado(CString ape, float s); //constructor común.
void Serialize(CArchive* archivo); //función Serialize a sobreescribir.
//...otras funciones miembros
};
Sobreescribir la función Serialize():
Sobreescribir una función significa escribir el contenido de una función que ha sido heredada para adaptarla a nuestras necesidades. La función Serialize() podría ser InLine, (o sea definida dentro de la clase), pero me parece más cómodo y claro definirla fuera de ella.
Aquí deberemos verificar si estamos escribiendo el objeto en el archivo o si lo estamos leyendo para así insertar o extraer los datos. A través de la función miembro, (heredada), IsStoring() sabremos si estamos guardando o leyendo, (IsStoring() retorna un valor distinto de 0 si precisamente se esta intentando guardar los datos del objeto), entonces podremos usar los operadores de inserción >> o extracción <<.
Veamos entonces nuestra función Serialize() sobreescrita:
void CEmpleado::Serialize( CArchive* archivo)
{
//Si estamos guardando los datos...
if( archivo->IsStoring() )
*archivo << Apellido << Salario;
else
*archivo >> Apellido >> Salario;
}
Macro IMPLEMENT_SERIAL():
Usando la macro IMPLEMENT_SERIAL() en nuestro archivo de implementación, (o sea el .cpp), podremos verificar que versión de nuestro programa estaremos usando, ya que esta macro recibe tres parámetros: el primero es el nombre de nuestra clase, (CEmpleado), el segundo el nombre de la clase de la cual ha sido derivada, (o sea CObject) y el tercero es un número mayor o igual a 0 que permite representar una versión del método de serialización que estamos aplicando. Ejemplo:
IMPLEMENT_SERIAL( CEmpleado, CObject, 1 )
Ejemplo:
Hasta aquí hemos visto cómo escribir una clase que permita la serialización heredándola de la clase CObject y sobreescribiendo la función Serialize(). Ahora nos queda implementar esta funcionalidad de nuestra clase. Supongamos que tenemos el siguiente diálogo:

Al pulsar el botón "Guardar" se escribirá la información de los cuadros de edición en el archivo "datos.dat" y al pulsar "Cargar" se leerá la información del archivo mostrándola en los statics del marco "Datos leídos". Botón "Guardar" entonces:
void
CSerializacionDlg::OnGuardar()
{
UpdateData(TRUE); //Paso datos de los
EDITs a las variables m_Apellido y m_Salario
CEmpleado miEmple(m_Apellido, m_Salario); //Se crea el objeto CEmpleado
CFileException e; //Objeto CFileException para interceptar errores
CFile* pFile = new CFile();
//Objeto CFile
char pErrorBuf[512];
ASSERT(pFile!=NULL); //¿no hay problemas con el objeto creado?
//Se abre datos.dat para escribir
if (pFile->Open ("datos.dat",
CFile::modeCreate|CFile::modeWrite | CFile::shareExclusive, &e)!=0)
{
//Se crea el objeto
CArchive a travéz del objeto CFile, pFile, para guardar
CArchive* pArchivo= new
CArchive(pFile, CArchive::store);
ASSERT(pArchivo!=NULL);
//¿Todo bien?
//Llamo a la función
Serialize de mi objeto CEmpleado
miEmple.Serialize (pArchivo);
//Cierro los archivos y
elimino los objetos, (fueron creados con new)
pArchivo->Close();
delete pArchivo;
pFile->Close();
delete pFile;
MessageBox("Info guardada con
éxito", "Guardar", MB_ICONINFORMATION);
}
else
{
//Si Open de CFile
devuelve 0, entonces muestro el error.
e.GetErrorMessage (pErrorBuf, 512, NULL);
AfxMessageBox(pErrorBuf );
}
}
Los pasos para poder guardar datos de un objeto en un archivo serían:
1) Crear un objeto CFile.
2) Abrir, (y/o crear), un archivo para escritura, (CFile::modeCreate|CFile::modeWrite).
3) Crear un objeto CArchive para "almacenar" a partir del objeto CFile, (CArchive* pArchivo= new CArchive(pFile, CArchive::store);).
4) Llamar a la función miembro Serialize() de nuestro objeto pasandole como parámetro el objeto CArchive.
5) Cerrar archivos y eliminar objetos CArchive y CFile.
De esta forma logramos guardar los datos ingresados, (note que de acuerdo a como abrimos nuestro archivo, modeCreate, cada vez que ocurre esta sentencia se sobreescribe lo anterior).
En el botón "Cargar" deberíamos hacer algo muy parecido, salvo que esta vez abrimos el archivo para lectura y el objeto CArchive que creamos será para cargar, CArchive::store.
void
CSerializacionDlg::OnCargar()
{
CEmpleado miEmple; //Objeto CEmpleado para poder usar la función Serialize
CFileException e;
//CFileException para interceptar errores
CFile* pFile = new CFile();
//Objeto CFile
char pErrorBuf[512];
//Donde se almacenará el
mensaje de error
ASSERT(pFile!=NULL);
//Se abre el archivo como lectura.
if (pFile->Open ("datos.dat",
CFile::modeNoTruncate|CFile::modeRead | CFile::shareExclusive, &e)!=0)
{
//Se crea el objeto
CArchive a partir de pFile y para cargar
CArchive* pArchivo = new
CArchive(pFile, CArchive::load);
ASSERT(pArchivo!=NULL);
//Llamamos a serialize
miEmple.Serialize (pArchivo);
//Cerramos y
"liquidamos" los objetos.
pArchivo->Close();
delete pArchivo;
pFile->Close();
delete pFile;
m_lblApellido=miEmple.Apellido ;
m_lblSalario.Format ("%.2f",
miEmple.Salario);
UpdateData(FALSE);
}
else
{
e.GetErrorMessage (pErrorBuf, 512, NULL);
AfxMessageBox(pErrorBuf );
}
}
Descargar fuente del ejemplo: serializacion.zip (29 Kb).