gpt4 book ai didi

c# - Protobuf-net 字段的惰性流式反序列化

转载 作者:行者123 更新时间:2023-11-30 17:46:37 24 4
gpt4 key购买 nike

总体目标:在反序列化时跳过一个很长的字段,并且在访问该字段时直接从流中读取元素而不加载整个字段。

示例类 被序列化/反序列化的对象是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 所在的流位置开始,我如何从随机索引中读取它?

注意:参数TFatArray2<T>可以是标有[ProtoContract]的其他类型,不仅仅是原语。也可能有多个 FatProperty2<T> 类型的属性在对象图中的不同深度。

方法二:序列化字段FatProperty2<T>在包含对象的序列化之后。所以,序列化FatPropertyClass使用长度前缀,然后使用长度前缀序列化它包含的所有胖数组。用属性标记所有这些胖数组属性,在反序列化时我们可以记住它们中的每一个的流位置。

那么问题是我们如何从中读取原语?这适用于使用 T item = Serializer.DeserializeItems<T>(sourceStream, PrefixStyle.Base128, Serializer.ListItemTag).Skip(index).Take(1).ToArray(); 的类获取索引 index 处的项目.但这对原语是如何工作的呢?一组基元似乎无法使用 DeserializeItems 反序列化。 .

DeserializeItems用 LINQ 那样用还行吗?它是否按照我假设的那样进行(在内部跳过流到正确的元素 - 最坏的情况是读取每个长度前缀并跳过它)?

问候,尤利安

最佳答案

这个问题在很大程度上取决于实际 模型 - 这不是图书馆专门针对方便的场景。我怀疑您最好的选择是使用 ProtoReader 手动编写阅读器.请注意,如果最外层对象是 List<SomeType>,则在读取选定项时一些技巧。或类似的,但内部对象通常被简单地读取或跳过。

通过 ProtoReader 从文档的根目录重新开始,您可以相当有效地查找第 n 个项目。如果您愿意,我稍后可以做一个具体的示例(除非您确定它确实有用,否则我不会跳进去)。作为引用,流的位置在这里没有用的原因是:库积极地过度读取和缓冲数据,除非您明确告诉它限制其长度。这是因为像“varint”这样的数据在没有大量缓冲的情况下很难有效读取,因为它最终会成为对 ReadByte() 的大量单独调用。 ,而不仅仅是使用本地缓冲区。


这是一个完全未经测试的版本,直接从读取器读取子属性的第 n 个数组项;请注意,一个接一个地多次调用它是低效的,但是如何将其更改为读取连续值范围等应该是显而易见的:

static int? ReadNthArrayItem(Stream source, int index, int maxLen)
{
using (var reader = new ProtoReader(source, null, null, maxLen))
{
int field, count = 0;
while ((field = reader.ReadFieldHeader()) > 0)
{
switch (field)
{
case 2: // fat property; a sub object
var tok = ProtoReader.StartSubItem(reader);
while ((field = reader.ReadFieldHeader()) > 0)
{
switch (field)
{
case 1: // the array field
if(count++ == index)
return reader.ReadInt32();
reader.SkipField();
break;
default:
reader.SkipField();
break;
}
}
ProtoReader.EndSubItem(tok, reader);
break;
default:
reader.SkipField();
break;
}
}
}
return null;
}

最后,请注意,如果这是一个大数组,您可能希望使用“打包”数组(请参阅 protobuf 文档,但这基本上存储它们时没有每个项目的标题)。这会更有效,但请注意,它需要稍微不同的读取代码。您可以通过添加 IsPacked = true 来启用压缩数组在 [ProtoMember(...)] 上对于那个数组。

关于c# - Protobuf-net 字段的惰性流式反序列化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25951775/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com