31.03.2009

Бинарнарный сериализатор. (BinarySerializer)

Данный сериализатор формирует из типа, байт последовательность. Формат последовательности следующий:

<class_element_count> - количество полей в классе (4 байта)
[<element>, <element>] - поля класса

где <element> - это простой тип данных:
<size> - размер тела (4 байта)
- простой тип переведенный в байты
или массив представление сериализованного массива аналогично классу (см. выше)

Данный сериализатор поддерживает следующие простые типы:
bool, short, int, long(Int64), float(Single), double, Guid.

Механизм содержит три основные части:
1. Атрибуты, которые указывают, что класс или поле необходимо сериализовать.
2. Временный контейнер, который используется для хранения данных при сериализации.
3. Собственно сам механизм сериализации.

Атрибуты:


// Атрибут для класса. Используется в BinarySerializer
[AttributeUsage(
AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
public sealed class ClassToBinaryAttribute : Attribute
{
}

/// Атрибут для поля класса. Используется в BinarySerializer
[AttributeUsage(
AttributeTargets.Field, Inherited = false, AllowMultiple = false)]
public sealed class FieldToBinaryAttribute : Attribute
{
}


Временный контейнер:


/// <summary>
/// Контейнер для хранение сериализованных бинарных данных
/// </summary>
public abstract class BinaryContainer
{
protected List<BinaryContainer> _childs =
new List<BinaryContainer>();
/// <summary>
/// Дочерние элементы контейнера
/// </summary>
public List<BinaryContainer> Childs
{
get { return this._childs; }
}

protected byte[] _body;
/// <summary>
/// Сериализованные данные
/// </summary>
public byte[] Body
{
//get { return _body; }
set { _body = value; }
}

/// <summary>
/// Получить размер бинарной информации
/// </summary>
/// <returns></returns>
public abstract int GetBodySize();

/// <summary>
/// Получить бинарные данные
/// </summary>
/// <returns></returns>
public abstract byte[] GetBody();
}

/// <summary>
/// Контейнер для поля
/// </summary>
class BinaryField : BinaryContainer
{
public override int GetBodySize()
{
if (this._body != null)
return this._body.Length + 4;
else
return 0;
}

public override byte[] GetBody()
{
if (this._body != null)
{
// весь размер буфера: размер + данные
int fullSize = this._body.Length + 4;
byte[] newBody = new byte[fullSize];

Buffer.BlockCopy(
BitConverter.GetBytes(this._body.Length), 0, newBody, 0, 4);
Buffer.BlockCopy(
this._body, 0, newBody, 4, this._body.Length);

return newBody;
}
else
return null;
}
}

/// <summary>
/// Контейнер для класса или массива
/// </summary>
class BinaryArray : BinaryContainer
{
public int ArrayElementCount
{
get { return this._childs.Count; }
}

public override int GetBodySize()
{
int size = 4;

if (this._childs.Count > 0)
{
foreach (BinaryContainer child in this._childs)
size += child.GetBodySize();

return size;
}
else
return size;
}

public override byte[] GetBody()
{
if (this._childs.Count > 0)
{
int bodySize = this.GetBodySize();
this._body = new byte[bodySize];

Buffer.BlockCopy(
BitConverter.GetBytes(
this.ArrayElementCount), 0, this._body, 0, 4);

int offset = 4;
foreach (BinaryContainer child in this._childs)
{
int childSize = child.GetBodySize();
byte[] childBody = child.GetBody();
Buffer.BlockCopy(
childBody, 0, this._body, offset, childSize);
offset += childSize;
}

return this._body;
}
else
return new byte[4];
}

}


Контейнер предоставляет два типа:
-тип для временного хранения простых типов (BinaryArray);
-тип для временного хранения сложных типов, таких как классы и массивы (BinaryField).
Два метода GetBodySize и GetBody предоставляют возможность для получения размера тела и сам байтовый массив. Паттерн Dependency Injection позволяет прозрачно, для основного механизма сериализации, использовать типы контейнера.

Сериализатор:


/// <summary>
/// Бинарный сериализатор
/// Поддержка следующих типов данных:
/// классы, массивы,
/// bool, short, int32, long(int64), float(single), double, Guid
/// Сереализация generic типов не поддерживается
/// </summary>
public class BinarySerializer
{
/// <summary>
/// Сериализация объекта
/// </summary>
/// <typeparam name="TType">Тип объекта</typeparam>
/// <param name="obj">Объект</param>
/// <returns>Массив байт</returns>
public byte[] Serialize<TType>(TType obj)
{
BinaryContainer binaryContainer = this.ClassSerializer(typeof(TType), obj);
byte[] result = binaryContainer.GetBody();

return result;
}

#region Хелперы для сериализации
/// <summary>
/// Сериализация объекта
/// </summary>
/// <param name="type">Тип объекта</param>
/// <param name="obj">Объект</param>
/// <returns>Временный контейнер</returns>
private BinaryContainer ClassSerializer(Type type, object obj)
{
// получение полей в типе
FieldInfo[] fInfos = type.GetFields(
BindingFlags.Instance |
BindingFlags.NonPublic |
BindingFlags.Public);

BinaryContainer classContainer = new BinaryArray();

foreach (FieldInfo fInfo in fInfos)
{
object[] customFieldAttributes =
fInfo.GetCustomAttributes(
ypeof(FieldToBinaryAttribute), false);

if (customFieldAttributes.Length == 1)
{
// получение аттрибута у типа поля
object[] customTypeAttributes =
fInfo.FieldType.GetCustomAttributes(
typeof(ClassToBinaryAttribute), false);

if (customTypeAttributes.Length == 1) // поле сложного типа
{
classContainer.Childs.Add(
this.ClassSerializer(fInfo.FieldType, fInfo.GetValue(obj)));
}
else // поле простого типа
{
if (fInfo.FieldType.IsArray) // поле массив
{
//сериализация массива
classContainer.Childs.Add(
this.ArraySerializer(fInfo, obj));
}
else // поле простого типа
classContainer.Childs.Add(
this.SimpleTypeSerializer(
fInfo.FieldType, fInfo.GetValue(obj)));
}
}
}

return classContainer;
}

/// <summary>
/// Сериализация массива
/// </summary>
/// <param name="fInfo">Дескриптор поля</param>
/// <param name="obj">Объект</param>
/// <returns>Бинарный контейнер с сереализованным массивом</returns>
private BinaryContainer ArraySerializer(FieldInfo fInfo, object obj)
{
// тип элемента массива
Type elementType = fInfo.FieldType.GetElementType();
Array array = (Array)fInfo.GetValue(obj);

bool isClass = false;
// получение аттрибута у типа поля
object[] customTypeAttributes =
elementType.GetCustomAttributes(
typeof(ClassToBinaryAttribute), false);

if (customTypeAttributes.Length == 1)
isClass = true;

// контейнер под хранения массива в бинарном виде
BinaryContainer binaryArray = new BinaryArray();

// испульзуем энумератор для передвижения по массиву
IEnumerator arrayEnumerator = array.GetEnumerator();
while (arrayEnumerator.MoveNext() &&
arrayEnumerator.Current != null)
{
BinaryContainer arrayElement = null;
if (isClass)
{
arrayElement =
this.ClassSerializer(
elementType, arrayEnumerator.Current);
}
else
{
// получение бинарного представления для элемента массива
arrayElement =
this.SimpleTypeSerializer(
elementType, arrayEnumerator.Current);
}

// добавление простого типа к бинарному контейнеру
binaryArray.Childs.Add(arrayElement);
}

return binaryArray;
}

/// <summary>
/// Сериализация простых типов
/// bool, short, int32, long(int64), float(single), double, Guid
/// </summary>
/// <param name="fieldType">Тип поля</param>
/// <param name="value">Значение поля</param>
/// <returns>Временный контейнер</returns>
private BinaryContainer SimpleTypeSerializer(Type fieldType, object value)
{
// буфер для хранения сериализовнных данных
byte[] buffer = null;

if (fieldType == typeof(Boolean))
{
buffer = BitConverter.GetBytes((Boolean)value);
}
else if (fieldType == typeof(Int16))
{
buffer = BitConverter.GetBytes((Int16)value);
}
else if (fieldType == typeof(Int32))
{
buffer = BitConverter.GetBytes((Int32)value);
}
else if (fieldType == typeof(Int64))
{
buffer = BitConverter.GetBytes((Int64)value);
}
else if (fieldType == typeof(Single))
{
buffer = BitConverter.GetBytes((Single)value);
}
else if (fieldType == typeof(Double))
{
buffer = BitConverter.GetBytes((Double)value);
}
else if (fieldType == typeof(Guid))
{
buffer = ((Guid)value).ToByteArray();
}

BinaryContainer tempC = null;
if (buffer != null && buffer.Length > 0)
{
tempC = new BinaryField();
tempC.Body = buffer;
}
else
throw new Exception("Ошибка в сериализациия поля типа " + fieldType.Name);

return tempC;
}
#endregion

/// <summary>
/// Десерилазиация
/// </summary>
/// <typeparam name="TType">Тип объекта</typeparam>
/// <param name="byteObj">Массив байт</param>
/// <returns>Объект заданного типа</returns>
public TType Deserialize<TType>(byte[] byteObj)
{
int offset = 0;
TType inst =
(TType)this.ClassDesirealize(
typeof(TType), byteObj, ref offset);

return inst;
}

#region Хелперы для десериализации

/// <summary>
/// Десериализация класса
/// </summary>
/// <param name="type">Тип класса</param>
/// <param name="data">Бинарные данные</param>
/// <returns>Экземпляр объекта</returns>
private object ClassDesirealize(
Type type, byte[] data, ref int offset)
{
// получение полей в типе
FieldInfo[] fInfos = type.GetFields(
BindingFlags.Instance |
BindingFlags.NonPublic |
BindingFlags.Public);

//создание объекта типа type
object obj = this.CreateInstance(type);

offset += 4; // первые 4 байта класса - количество полей

foreach (FieldInfo fInfo in fInfos)
{
object[] customFieldAttributes =
fInfo.GetCustomAttributes(
typeof(FieldToBinaryAttribute), false);

if (customFieldAttributes.Length == 1)
{
// получение аттрибута у типа поля
object[] customTypeAttributes =
fInfo.FieldType.GetCustomAttributes(
typeof(ClassToBinaryAttribute), false);

object fieldValue = null;

if (customTypeAttributes.Length == 1) // поле сложного типа
{
fieldValue =
this.ClassDesirealize(
fInfo.FieldType, data, ref offset);
}
else
{
if (fInfo.FieldType.IsArray) // поле массив
{
fieldValue =
this.ArrayDeserializer(fInfo, data, ref offset);
}
else // поле простого типа
{
// массив байт для хранения размера
byte[] sizeByte = new byte[4];
Buffer.BlockCopy(
data, offset, sizeByte, 0, sizeByte.Length);
// получение размера
int size = BitConverter.ToInt32(sizeByte, 0);

// получение данных
byte[] dataBuffer = new byte[size];
Buffer.BlockCopy(
data, offset + sizeByte.Length,
dataBuffer, 0, dataBuffer.Length);

// десериализация поля
fieldValue =
this.SimpleTypeDeserializer(
fInfo.FieldType, dataBuffer);

// увелечени сдвига по буферу
offset += sizeByte.Length + size;
}
}

// помещение значение в объект
fInfo.SetValue(obj, fieldValue);
}
}

return obj;
}

/// <summary>
/// Десериализация массива
/// </summary>
/// <param name="fInfo">Дескриптор поля</param>
/// <param name="data">Массив байт</param>
/// <param name="offset">Смещение в массиве байт</param>
/// <returns>Массив</returns>
private object ArrayDeserializer(
FieldInfo fInfo, byte[] data, ref int offset)
{
// получение количества элементов в массиве
byte[] elCountByte = new byte[4];
Buffer.BlockCopy(data, offset, elCountByte, 0, elCountByte.Length);
int elementsCount = BitConverter.ToInt32(elCountByte, 0);

// увелечение счетчика движения по буферу
offset += elCountByte.Length;

bool isClass = false;
// тип элемента массива
Type elementType = fInfo.FieldType.GetElementType();
// получение аттрибута у типа поля
object[] customTypeAttributes =
elementType.GetCustomAttributes(
typeof(ClassToBinaryAttribute), false);
if (customTypeAttributes.Length == 1)
isClass = true;

// создание экземпляра массива
Array array = Array.CreateInstance(elementType, elementsCount);

//получение элементов массива
for (int i = 0; i < elementsCount; i++)
{
object value = null;

if (isClass)
{
value =
this.ClassDesirealize(
elementType, data, ref offset);
}
else
{
if (elementType.IsArray)
{
throw new NotImplementedException(
"Ошибка при десериализации в массива ");
}
else
{
// получение размера элемента массива
byte[] elSizeByte = new byte[4];
Buffer.BlockCopy(
data, offset, elSizeByte, 0, elSizeByte.Length);
int elementSize = BitConverter.ToInt32(elSizeByte, 0);

offset += elSizeByte.Length;

//получение значения элемента массива
byte[] valueByte = new byte[elementSize];
Buffer.BlockCopy(
data, offset, valueByte, 0, valueByte.Length);
value = this.SimpleTypeDeserializer(elementType, valueByte);

offset += valueByte.Length;
}
}

// заполнение массива
array.SetValue(value, i);
}

return array;
}

/// <summary>
/// Создание экземпляра класса по его типу
/// </summary>
/// <param name="type">Тип</param>
/// <returns>Объект</returns>
private object CreateInstance(Type type)
{
ConstructorInfo constructor =
type.GetConstructor(new Type[] { });

object inst = null;

if (constructor != null)
{
inst = constructor.Invoke(new object[] { });
}
else
throw new Exception(
"Не возможно создать объект типа " +
type.Name +
". Объект должен иметь пустой конструктор");

return inst;
}

/// <summary>
/// Десериализация простых типов
/// bool, short, int32, long(int64), float(single), double, Guid
/// </summary>
/// <param name="fieldType"></param>
/// <param name="data"></param>
/// <returns></returns>
public object SimpleTypeDeserializer(Type fieldType, byte[] data)
{
object value = null;

if (fieldType == typeof(Boolean))
{
value = BitConverter.ToBoolean(data, 0);
}
else if (fieldType == typeof(Int16))
{
value = BitConverter.ToInt16(data, 0);
}
else if (fieldType == typeof(Int32))
{
value = BitConverter.ToInt32(data, 0);
}
else if (fieldType == typeof(Int64))
{
value = BitConverter.ToInt64(data, 0);
}
else if (fieldType == typeof(Single))
{
value = BitConverter.ToSingle(data, 0);
}
else if (fieldType == typeof(Double))
{
value = BitConverter.ToDouble(data, 0);
}
else if (fieldType == typeof(Guid))
{
value = new Guid(data);
}

if (value == null)
throw new Exception(
"Ошибка в десериализациия поля типа " +
fieldType.Name);

return value;
}

#endregion

}

1 комментарий:

gt_rocker комментирует...
Этот комментарий был удален администратором блога.