天天看點

.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許可證。