天天看点

.NET Protobuf包装器库

关于

需求

安装

用法

序列化

反序列化

字段定义

字段排序

非空构造函数对象

获取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.

可以使用<code>Wodsoft.Protobuf.Message</code>类中的静态方法<code>Serialize</code>。

你需要一个<code>System.IO.Stream</code>来存储序列化后的数据。

这里也有一个重载方法。

你可以传递一个<code>Google.Protobuf.CodedInputStream</code>来替代<code>System.IO.Stream</code>。

或者你想直接拿到序列化后的字节数组。

你可以使用<code>Wodsoft.Protobuf.Message</code>类中的静态方法<code>Deserialize</code>。

你需要传递包含需要反序列化数据的<code>System.IO.Stream</code>。

它将返回你的泛型对象<code>T</code>。

你可以传递一个<code>Google.Protobuf.CodedOutputStream</code>来替代<code>System.IO.Stream</code>。

或者你想直接从字节数组进行反序列化。

<code>IMessageFieldProvider.GetFields(Type type)</code>会返回从对象映射而来的消息字段。

默认实现是<code>GeneralMessageFieldProvider.Intance</code>类。

它只会映射可读写的属性到消息字段。

你可以创建自己的<code>IMessageFieldProvider</code>去映射消息字段。

然后通过设置静态属性<code>Message&lt;T&gt;.FieldProvider</code>为自定义的<code>IMessageFieldProvider</code>。

你需要为每个需要自定义消息字段的类型设置<code>IMessageFieldProvider</code>。

给属性添加<code>System.Runtime.Serialization.DataMemberAttribute</code>特性然后设置<code>Order</code>属性。

不然将根据属性名称进行排序。

⚠️ 如果有任何一个属性使用了<code>DataMemberAttribute</code>特性,将只会序列化拥有<code>DataMemberAttribute</code>特性的属性。
⚠️ 如果全部没有使用<code>DataMemberAttribute</code>特性,服务如果因为部署问题使用了不同版本的模型,反序列化时可能因为字段排序问题存在错误。

通过调用静态方法<code>MessageBuilder.SetTypeInitializer&lt;T&gt;(Func&lt;T&gt; initializer)</code>来设置对象初始化委托。

我们可以直接转换模型对象为<code>Message&lt;&gt;</code>。

然后这个<code>message</code>可以直接被Protobuf序列化。

C#类型

Protobuf类型

消息结构

bool(?)

bool

Varint

sbyte(?)

int32

byte(?)

short(?)

ushort(?)

int(?)

long(?)

int64

uint(?)

uint32

ulong(?)

uint64

float(?)

float

double(?)

double

string

Length-delimited

byte[]

ByteString

Guid(?)

DateTime(?)

google.protobuf.Timestamp

DateTimeOffset(?)

TimeSpan(?)

google.protobuf.Duration

IMessage

T[]

RepeatedField&lt;T&gt;

ICollection&lt;T&gt;

Collection&lt;T&gt;

IList&lt;T&gt;

List&lt;T&gt;

IDictionary&lt;TKey, TValue&gt;

MapField&lt;TKey, TValue&gt;

Dictionary&lt;TKey, TValue&gt;

(?) 意思是可以为<code>Nullable&lt;&gt;</code>可空类型。

可以直接使用继承了<code>Google.Protobuf.IMessage</code>的Protobuf对象作为属性类型。

所有<code>RepeatedField</code>与<code>MapField</code>对象不能包含<code>null</code>值。

支持<code>byte</code>,<code>sbyte</code>,<code>short</code>和<code>ushort</code>作为属性类型。

它们将作为<code>int</code>类型进行序列化。

如果从其它第三方来源数据进行反序列化,<code>int</code>可能会丢失数据。

首先,Protobuf通过<code>Google.Protobuf.IMessage</code>与<code>Google.Protobuf.IBufferMessage</code>接口进行序列化工作。

我们定义了一个抽象类<code>Wodsoft.Protobuf.Message</code>。

然后定义抽象保护方法<code>Read</code>,<code>Write</code>,<code>CalculateSize</code>。

显式实现这些接口并调用这些方法。

然后定义泛型抽象类<code>Wodsoft.Protobuf.Message&lt;T&gt;</code>。

这里有一个属性可以直接获取到原始类型值。然后我们实现了一些隐式转换操作。

最后,为需要序列化的类型动态创建继承了<code>Message&lt;T&gt;</code>的类。

通过Emit动态创建代码实现<code>Read</code>,<code>Write</code>,<code>CalculateSize</code>方法。

建议使用 <code>RepeatedField&lt;&gt;</code>,<code>IList&lt;&gt;</code>或<code>ICollection&lt;&gt;</code>作为集合属性的类型。

使用<code>RepeatedField&lt;&gt;</code>会获得最佳性能(因为不需要额外类型转换)。

使用<code>IList&lt;&gt;</code>或<code>ICollection&lt;&gt;</code>在序列化时会转换为<code>RepeatedField&lt;&gt;</code>。

使用<code>List&lt;&gt;</code>或<code>Collection&lt;&gt;</code>在序列化时会转换为<code>RepeatedField&lt;&gt;</code>。

并且在反序列化时会转换回<code>List&lt;&gt;</code>或<code>Collection&lt;&gt;</code>(上一个会直接返回<code>RepeatedField&lt;&gt;</code>)。

推荐使用 <code>MapField&lt;,&gt;</code>或<code>IDictionary&lt;,&gt;</code>作为字典属性的类型。

使用<code>MapField&lt;,&gt;</code>会获得最佳性能。

使用<code>IDictionary&lt;,&gt;</code>在序列化时会转换为<code>MapField&lt;,&gt;</code>。

使用<code>Dictionary&lt;,&gt;</code>在序列化时会转换为<code>MapField&lt;,&gt;</code>。

并且在反序列化时会转换回<code>Dictionary&lt;,&gt;</code>(上一个会直接返回<code>MapField&lt;,&gt;</code>)。

库使用MIT许可证。