.NET Protobuf包装器库
Wodsoft Protobuf Wrapper
内容
- 关于
- 需求
- 安装
- 用法
- 序列化
- 反序列化
- 字段定义
- 字段排序
- 非空构造函数对象
- 获取Protobuf包装器
- 高级
- 支持的属性类型与Protobuf类型的关系
- 如何工作
- 性能
- 许可证
关于
这是一个可以帮助你不需要.proto文件就能够使用Protobuf序列化的一个库。
通常.proto文件会创建继承IMessage接口的模型,Protobuf使用这些模型来进行序列化。
有时候我们已经在自己的.NET项目里创建了一些模型,但我们需要使用Protobuf对这些模型进行序列化。
这时候这个库就能帮助你使用Protobuf对已存在的模型进行序列化。
Github地址:Wodsoft.Protobuf.Wrapper
需求
Wodsoft.Protobuf.Wrapper需要NETStandard 2.0或以上。
这个库需要工作在允许动态代码编译的平台。所以IOS不支持。
安装
在NuGet上获取Wodsoft.Protobuf.Wrapper.
dotnet add package Wodsoft.Protobuf.Wrapper
用法
序列化
可以使用Wodsoft.Protobuf.Message类中的静态方法Serialize。
你需要一个System.IO.Stream来存储序列化后的数据。
YourModel model = new ();
MemoryStream stream = new MemoryStream();
Message.Serialize(stream, model);
这里也有一个重载方法。
你可以传递一个Google.Protobuf.CodedInputStream来替代System.IO.Stream。
YourModel model = new ();
CodedInputStream input = ...;
Message.Serialize(input, model);
或者你想直接拿到序列化后的字节数组。
YourModel model = new ();
var bytes = Message.SerializeToBytes(model);
反序列化
你可以使用Wodsoft.Protobuf.Message类中的静态方法Deserialize。
你需要传递包含需要反序列化数据的System.IO.Stream。
它将返回你的泛型对象T。
Stream stream = ...;
YourType model = Message.Deserialize(stream);
这里也有一个重载方法。
你可以传递一个Google.Protobuf.CodedOutputStream来替代System.IO.Stream。
CodedOutputStream output = ...;
YourType model = Message.Deserialize(output);
或者你想直接从字节数组进行反序列化。
YourType model = Message.DeserializeFromBytes(bytes);
字段定义
IMessageFieldProvider.GetFields(Type type)会返回从对象映射而来的消息字段。
默认实现是GeneralMessageFieldProvider.Intance类。
它只会映射可读写的属性到消息字段。
你可以创建自己的IMessageFieldProvider去映射消息字段。
然后通过设置静态属性Message为自定义的IMessageFieldProvider。
你需要为每个需要自定义消息字段的类型设置
IMessageFieldProvider。
字段排序
给属性添加System.Runtime.Serialization.DataMemberAttribute特性然后设置Order属性。
不然将根据属性名称进行排序。
?? 如果有任何一个属性使用了
DataMemberAttribute特性,将只会序列化拥有DataMemberAttribute特性的属性。
?? 如果全部没有使用
DataMemberAttribute特性,服务如果因为部署问题使用了不同版本的模型,反序列化时可能因为字段排序问题存在错误。
非空构造函数
通过调用静态方法MessageBuilder.SetTypeInitializer来设置对象初始化委托。
获取Protobuf包装器
我们可以直接转换模型对象为Message<>。
SimplyModel model;
Message message = model;
然后这个message可以直接被Protobuf序列化。
高级
支持的属性类型与Protobuf类型的关系
| C#类型 | Protobuf类型 | 消息结构 |
|---|---|---|
| bool(?) | bool | Varint |
| sbyte(?) | int32 | Varint |
| byte(?) | int32 | Varint |
| short(?) | int32 | Varint |
| ushort(?) | int32 | Varint |
| int(?) | int32 | Varint |
| long(?) | int64 | Varint |
| uint(?) | uint32 | Varint |
| ulong(?) | uint64 | Varint |
| float(?) | float | Varint |
| double(?) | double | Varint |
| string | string | Length-delimited |
| byte[] | ByteString | Length-delimited |
| Guid(?) | ByteString | Length-delimited |
| DateTime(?) | google.protobuf.Timestamp | Length-delimited |
| DateTimeOffset(?) | google.protobuf.Timestamp | Length-delimited |
| TimeSpan(?) | google.protobuf.Duration | Length-delimited |
| IMessage | Length-delimited | |
| T[] | RepeatedField |
Length-delimited |
| ICollection |
RepeatedField |
Length-delimited |
| Collection |
RepeatedField |
Length-delimited |
| IList |
RepeatedField |
Length-delimited |
| List |
RepeatedField |
Length-delimited |
| IDictionary |
MapField |
Length-delimited |
| Dictionary |
MapField |
Length-delimited |
- (?) 意思是可以为
Nullable<>可空类型。 - 可以直接使用继承了
Google.Protobuf.IMessage的Protobuf对象作为属性类型。 - 所有
RepeatedField与MapField对象不能包含null值。 - 支持
byte,sbyte,short和ushort作为属性类型。
它们将作为int类型进行序列化。
如果从其它第三方来源数据进行反序列化,int可能会丢失数据。
如何工作
首先,Protobuf通过Google.Protobuf.IMessage与Google.Protobuf.IBufferMessage接口进行序列化工作。
我们定义了一个抽象类Wodsoft.Protobuf.Message。
然后定义抽象保护方法Read,Write,CalculateSize。
显式实现这些接口并调用这些方法。
然后定义泛型抽象类Wodsoft.Protobuf.Message。
这里有一个属性可以直接获取到原始类型值。然后我们实现了一些隐式转换操作。
public T Source { get; }
最后,为需要序列化的类型动态创建继承了Message的类。
通过Emit动态创建代码实现Read,Write,CalculateSize方法。
性能
- 建议使用
RepeatedField<>,IList<>或ICollection<>作为集合属性的类型。
使用RepeatedField<>会获得最佳性能(因为不需要额外类型转换)。 - 使用
IList<>或ICollection<>在序列化时会转换为RepeatedField<>。 - 使用
List<>或Collection<>在序列化时会转换为RepeatedField<>。
并且在反序列化时会转换回List<>或Collection<>(上一个会直接返回RepeatedField<>)。 - 推荐使用
MapField<,>或IDictionary<,>作为字典属性的类型。
使用MapField<,>会获得最佳性能。 - 使用
IDictionary<,>在序列化时会转换为MapField<,>。 - 使用
Dictionary<,>在序列化时会转换为MapField<,>。
并且在反序列化时会转换回Dictionary<,>(上一个会直接返回MapField<,>)。
许可证
库使用MIT许可证。