Общая цель: пропустить очень длинное поле при десериализации и при обращении к полю для чтения элементов прямо из потока без загрузки всего поля.
Примеры классов Сериализуемый / десериализуемый объект - FatPropertyClass
.
[ProtoContract]
public class FatPropertyClass
{
[ProtoMember(1)]
private int smallProperty;
[ProtoMember(2)]
private FatArray2<int> fatProperty;
[ProtoMember(3)]
private int[] array;
public FatPropertyClass()
{
}
public FatPropertyClass(int sp, int[] fp)
{
smallProperty = sp;
fatProperty = new FatArray<int>(fp);
}
public int SmallProperty
{
get { return smallProperty; }
set { smallProperty = value; }
}
public FatArray<int> FatProperty
{
get { return fatProperty; }
set { fatProperty = value; }
}
public int[] Array
{
get { return array; }
set { array = value; }
}
}
[ProtoContract]
public class FatArray2<T>
{
[ProtoMember(1, DataFormat = DataFormat.FixedSize)]
private T[] array;
private Stream sourceStream;
private long position;
public FatArray2()
{
}
public FatArray2(T[] array)
{
this.array = new T[array.Length];
Array.Copy(array, this.array, array.Length);
}
[ProtoBeforeDeserialization]
private void BeforeDeserialize(SerializationContext context)
{
position = ((Stream)context.Context).Position;
}
public T this[int index]
{
get
{
// logic to get the relevant index from the stream.
return default(T);
}
set
{
// only relevant when full array is available for example.
}
}
}
Я могу десериализовать так: FatPropertyClass d = model.Deserialize(fileStream, null, typeof(FatPropertyClass), new SerializationContext() {Context = fileStream}) as FatPropertyClass;
где model
может быть, например:
RuntimeTypeModel model = RuntimeTypeModel.Create();
MetaType mt = model.Add(typeof(FatPropertyClass), false);
mt.AddField(1, "smallProperty");
mt.AddField(2, "fatProperty");
mt.AddField(3, "array");
MetaType mtFat = model.Add(typeof(FatArray<int>), false);
Это пропустит десериализацию array
в FatArray<T>
. Однако позже мне нужно будет прочитать случайные элементы из этого массива. Я пытался запомнить позицию потока перед десериализацией в BeforeDeserialize(SerializationContext context)
методе FatArray2<T>
. Как в приведенном выше коде: position = ((Stream)context.Context).Position;
. Однако, похоже, это всегда конец потока.
Как я могу запомнить позицию потока, с которой начинается FatProperty2
, и как я могу читать из него по случайному индексу?
Примечание. Параметр T
в FatArray2<T>
может относиться к другим типам, отмеченным [ProtoContract]
, а не только к примитивам. Также может быть несколько свойств типа FatProperty2<T>
на разной глубине в графе объекта.
Метод 2: сериализуйте поле FatProperty2<T>
после сериализации содержащего объекта. Итак, сериализуйте FatPropertyClass
с префиксом длины, затем сериализуйте с префиксом длины все содержащиеся в нем массивы. Отметьте все эти свойства толстого массива атрибутом, и при десериализации мы сможем запомнить положение потока для каждого из них.
Тогда возникает вопрос, как нам прочитать из него примитивы? Это нормально работает для классов, использующих T item = Serializer.DeserializeItems<T>(sourceStream, PrefixStyle.Base128, Serializer.ListItemTag).Skip(index).Take(1).ToArray();
, чтобы получить элемент по индексу index
. Но как это работает для примитивов? Кажется, что массив примитивов не может быть десериализован с помощью DeserializeItems
.
DeserializeItems
с LINQ даже нормально используется? Делает ли он то, что я предполагаю (внутренне пропускает поток к нужному элементу - в худшем случае читает каждый префикс длины и пропускает его)?
С уважением, Юлиан