我正在将我的一些 DataContractSerializer 使用切换到协议缓冲区序列化(特别是使用 protobuf-net),目标是更快的序列化和更小的序列化数据大小以存储在数据库 blob 中。
我发现更改我的对象模型对消息大小有很大影响。我认为这意味着由于我选择了对象模型,我的序列化数据被人为地夸大了,我想解决这个问题。
具体来说,我的问题是:我可以改变我的 protobuf-net 使用,或者可能是序列化库,以获得更小的消息大小吗?我将给出一个对象模型以及到目前为止我能够弄清楚的内容。
就我而言,我正在序列化 OCR 数据......这是一个简化的对象模型:
[ProtoContract(SkipConstructor = true, UseProtoMembersOnly = true)]
public class OcrTable
{
[ProtoMember(1)]
public List<OcrTableCell> Cells;
}
[ProtoContract(SkipConstructor = true, UseProtoMembersOnly = true)]
public class OcrTableCell
{
[ProtoMember(1)]
public int Row;
[ProtoMember(2)]
public int Column;
[ProtoMember(3)]
public int RowSpan;
//...
[ProtoMember(10)]
public int Height;
[ProtoMember(11)]
public List<OcrCharacter> Characters;
}
[ProtoContract(SkipConstructor = true, UseProtoMembersOnly = true)]
public class OcrCharacter
{
[ProtoMember(1)]
public int Code;
[ProtoMember(2)]
public int Data;
[ProtoMember(3)]
public int Confidence;
//...
[ProtoMember(11)]
public int Width;
}
由于数据最终只是一堆相关的原语(主要是int
's),我认为打包位序列化的好处会有所帮助,但在当前的类结构中,所有实际列表都是自定义类型。
为了允许打包位序列化,我修改了完全删除自定义类型,并有多个原语列表,按它们的顺序关联。例如:
[ProtoContract(SkipConstructor = true, UseProtoMembersOnly = true)]
public class OcrTableCell
{
[ProtoMember(1)]
public int Row;
//...
[ProtoMember(10)]
public int Height;
[ProtoMember(11, IsPacked=true)]
public List<int> CharacterCode;
[ProtoMember(12, IsPacked=true)]
public List<int> CharacterData;
//...
[ProtoMember(21, IsPacked=true)]
public List<int> CharacterWidth;
}
在这里你可以看到 I 替换List<OcrCharacter>
为多个列表:每个字段对应一个OcrCharacter
. 这对序列化数据大小有相当大的影响,在某些情况下减少了三分之二(即使在 gzip 之后)。
我认为仅仅为了支持序列化而对我的对象模型进行这样的更改是不切实际的......并且保留第二个“帮助”模型来准备序列化似乎是不可取的。
仍然让我感到困扰的是,我仅仅因为数据的对象模型而人为地夸大了序列化数据大小。
序列化参数或库是否有更好的选择来序列化这种类型的对象图?我确实尝试设置应用于列表DataFormat=DataFormat.Group
的属性,但看到消息大小的 0 变化让我感到困惑。ProtoMember