Serialize an object is not very difficult, but we have to understand why I decided to implement some features, so my goal is:
- Simple load and save data on a file
- Client-Server data communication
so I have two thinks to take care:
- Some field may be DBNull (not null)
- DataRows have a row state to keep
For first problem I use this great serialize-deserialize code from CodeProject, A Raw Deserializer; this supports null value serializing.
The second is directly implemeted.
There are two methods:
public static byte[] DataSetToBytes(DataSet ds)
public static DataSet BytesToDataSet(byte[] data)
Here complete code:
public static byte[] DataSetToBytes(DataSet ds)
{
//Default preCode to test byte array content
return DataSetToBytes(ds, 48); // '0'
}
public static byte[] DataSetToBytes(DataSet ds, byte preCode)
{
//preCode is to test byte array content
bool bDeleted = false;
MemoryStream ms;
RawSerializer rs;
ms = new MemoryStream();
rs = new RawSerializer(ms);
ms.WriteByte(preCode); // first write precode
rs.Serialize(ds.Tables[0].TableName); // serialize tablename
rs.Serialize(ds.Tables[0].Columns.Count); // serialize columns count
rs.Serialize(ds.Tables[0].Rows.Count); // serialize rows count
// serialize columns info
foreach (DataColumn dc in ds.Tables[0].Columns)
{
rs.Serialize(dc.ColumnName);
rs.Serialize(dc.AllowDBNull);
rs.Serialize(dc.DataType.AssemblyQualifiedName);
}
// serialize data
foreach (DataRow dr in ds.Tables[0].Rows)
{
// check deleted state and set row deleted flag
if (dr.RowState == DataRowState.Deleted)
{
// reject changes to access deleted data
dr.RejectChanges();
bDeleted = true;
}
else
{
bDeleted = false;
}
foreach (DataColumn dc in ds.Tables[0].Columns)
{
//serialize field
if (dc.AllowDBNull)
{
rs.SerializeNullable(dr[dc]);
}
else
{
rs.Serialize(dr[dc]);
}
}
//serialize row state
switch (dr.RowState)
{
case DataRowState.Added:
rs.Serialize((byte)1);
break;
case DataRowState.Modified:
rs.Serialize((byte)2);
break;
default:
if (bDeleted)
{
rs.Serialize((byte)3);
}
else
{
rs.Serialize((byte)0);
}
break;
}
}
rs.Flush();
ms.Position = 0;
return ms.ToArray();
}
public static DataSet BytesToDataSet(byte[] data)
{
//dummy
if (data.Length == 0) return null;
MemoryStream ms;
RawDeserializer rd;
ms = new MemoryStream();
rd = new RawDeserializer(ms);
//ignore precode on first byte
ms.Write(data, 1, data.Length - 1);
ms.Position = 0;
int rowState = 0;
string tableName = rd.DeserializeString(); // deserialize table name
int columns = rd.DeserializeInt(); // deserialize columns count
int rows = rd.DeserializeInt(); // deserialize rows count
DataSet dsIn = new DataSet();
dsIn.Tables.Add(tableName);
// deserialize columns with columns attribs
for (int x = 0; x < columns; x++)
{
string columnName = rd.DeserializeString();
bool allowNulls = rd.DeserializeBool();
string type = rd.DeserializeString();
DataColumn dc = new DataColumn(columnName, Type.GetType(type));
dc.AllowDBNull = allowNulls;
dsIn.Tables[0].Columns.Add(dc);
}
// deserialize data
for (int y = 0; y < rows; y++)
{
DataRow dr = dsIn.Tables[0].NewRow();
// deserialize fields
for (int x = 0; x < columns; x++)
{
DataColumn dc = dsIn.Tables[0].Columns[x];
object obj;
if (dc.AllowDBNull)
{
obj = rd.DeserializeNullable(dc.DataType);
}
else
{
obj = rd.Deserialize(dc.DataType);
}
dr[dc] = obj;
}
//add new row, now this row is in Added state
dsIn.Tables[0].Rows.Add(dr);
// force real row state
rowState = rd.DeserializeByte();
switch (rowState)
{
case 0:
dsIn.Tables[0].Rows[y].AcceptChanges();
break;
case 1:
//dsIn.Tables[0].Rows[y].SetAdded();
break;
case 2:
dsIn.Tables[0].Rows[y].AcceptChanges();
dsIn.Tables[0].Rows[y].SetModified();
break;
case 3:
dsIn.Tables[0].Rows[y].AcceptChanges();
dsIn.Tables[0].Rows[y].Delete();
break;
}
}
return dsIn;
}