快速序列化組件MessagePack介紹
簡介
MessagePack for C#(MessagePack-CSharp)是用於C#的極速MessagePack序列化程序,比MsgPack-Cli快10倍,與其他所有C#序列化程序相比,具有最好的性能。 MessagePack for C#具有內置的LZ4壓縮功能,可以實現超快速序列化和二進制占用空間小。 性能永遠是重要的! 可用於遊戲,分布式計算,微服務,數據存儲到Redis等。支持.NET, .NET Core, Unity, Xamarin。
從上圖我們看出MessagePack for C#在性能測試中是最好的,這裏解釋一下第三個MsgPack-Cli是MessagePack官方實現的。第一和第二都是MessagePack for C#,第一項相比第二項具有稍快一點的序列化和反序列化速度,但是第二項采用了L4壓縮功能,顯著的減少了二進制的大小。在實際使用中推薦使用L4壓縮功能。
使用
該組件已經發布在Nuget,使用命令加入項目。
Install-Package MessagePack
分析器
Install-Package MessagePackAnalyzer
擴展
Install-Package MessagePack.ImmutableCollection
Install-Package MessagePack.ReactiveProperty
Install-Package MessagePack.UnityShims
Install-Package MessagePack.AspNetCoreMvcFormatter
Unity在此處下載 https://github.com/neuecc/MessagePack-CSharp/releases
快速開始
定義一個類添加[MessagePackObject]
特性,公共成員(屬性或者字段)添加[Key]
特性,調用MessagePackSerializer.Serialize<T>/Deserialize<T>
進行序列化和反序列化,ToJson
可以幫我們轉儲二進制為json格式。
// 標記 MessagePackObjectAttribute [MessagePackObject] public class MyClass { // Key 是序列化索引,對於版本控制非常重要。 [Key(0)] public int Age { get; set; } [Key(1)] public string FirstName { get; set; } [Key(2)] public string LastName { get; set; } // 公共成員中不序列化目標,標記IgnoreMemberAttribute [IgnoreMember] public string FullName { get { return FirstName + LastName; } } }
class Program
{
static void Main(string[] args)
{
var mc = new MyClass
{
Age = 99,
FirstName = "hoge",
LastName = "huga",
};
// 序列化
var bytes = MessagePackSerializer.Serialize(mc);
//反序列化
var mc2 = MessagePackSerializer.Deserialize<MyClass>(bytes);
// 你可以將msgpack二進制轉儲為可讀的json。
// 在默認情況下,MeesagePack for C#減少了屬性名稱信息。
// [99,"hoge","huga"]
var json = MessagePackSerializer.ToJson(bytes);
Console.WriteLine(json);
Console.ReadKey();
}
}
序列化索引將會影響該信息在序列化數據中的位置
默認情況下特性是必須的,但是我們有方法進行改變,讓它變為不是必須的,詳情請看後面。
分析器
MessagePackAnalyzer
可以幫助我們定義對象. 如果不符合規則,那麽特性, 程序集等可以被檢測到,如果我們編譯就會出現編譯錯誤。
如果要允許特定類型(例如,註冊自定義類型時),請將MessagePackAnalyzer.json放在項目根目錄下,並將生成操作設置為AdditionalFiles
(其他文件)。
這是MessagePackAnalyzer.json內容的一個示例。
[ "MyNamespace.FooClass", "MyNameSpace.BarStruct" ]
內置的支持類型
這些類型可以默認序列化。
基元(int、string等等), Enum, Nullable<>, TimeSpan, DateTime, DateTimeOffset, Nil, Guid, Uri, Version, StringBuilder, BitArray, ArraySegment<>, BigInteger, Complext, Task, Array[], Array[,], Array[,,], Array[,,,], KeyValuePair<,>, Tuple<,...>, ValueTuple<,...>, List<>, LinkedList<>, Queue<>, Stack<>, HashSet<>, ReadOnlyCollection<>, IList<>, ICollection<>, IEnumerable<>, Dictionary<,>, IDictionary<,>, SortedDictionary<,>, SortedList<,>, ILookup<,>, IGrouping<,>, ObservableCollection<>, ReadOnlyOnservableCollection<>, IReadOnlyList<>, IReadOnlyCollection<>, ISet<>, ConcurrentBag<>, ConcurrentQueue<>, ConcurrentStack<>, ReadOnlyDictionary<,>, IReadOnlyDictionary<,>, ConcurrentDictionary<,>, Lazy<>, Task<>, 自定義繼承ICollection <>或IDictionary <,>具有無參構造方法, IList,IDictionary和自定義繼承ICollection或IDictionary具有無參構造函數(包括ArrayList和Hashtable)。
您可以添加自定義類型的支持和一些官方/第三方擴展包。 對於ImmutableCollections(ImmutableList <>等),對於ReactiveProperty和Unity(Vector3, Quaternion等等),對於F#(Record,FsList,Discriminated Unions等)。
MessagePack.Nil
是MessagePack for C#的內置null/void/unit表示類型。
對象序列化
MessagePack for C#可以序列化public Class或Struct,序列化目標必須標記[MessagePackObject]和[Key], Key類型可以選擇int或字符串。如果Key類型是int,則使用序列化格式為數組,如果Key類型是字符串,則使用序列化格式為鍵值對,如果您定義了[MessagePackObject(keyAsPropertyName:true)],則不需要Key特性。
[MessagePackObject]
public class Sample1
{
[Key(0)]
public int Foo { get; set; }
[Key(1)]
public int Bar { get; set; }
}
[MessagePackObject]
public class Sample2
{
[Key("foo")]
public int Foo { get; set; }
[Key("bar")]
public int Bar { get; set; }
}
[MessagePackObject(keyAsPropertyName: true)]
public class Sample3
{
// 不需要key特性
public int Foo { get; set; }
// 不需要序列化的成員使用IgnoreMember特性
[IgnoreMember]
public int Bar { get; set; }
}
// 結果 [10,20]
Console.WriteLine(MessagePackSerializer.ToJson(new Sample1 { Foo = 10, Bar = 20 }));
// 結果 {"foo":10,"bar":20}
Console.WriteLine(MessagePackSerializer.ToJson(new Sample2 { Foo = 10, Bar = 20 }));
// 結果 {"Foo":10}
Console.WriteLine(MessagePackSerializer.ToJson(new Sample3 { Foo = 10, Bar = 20 }));
所有模式序列化目標都是公共實例成員(字段或屬性)。 如果要避免序列化目標,可以將[IgnoreMember]
添加到目標成員。
目標類必須是 public, 不允許 private, internal 類.
應該使用哪種Key類型,int或string? 作者建議使用int key,因為比string key更快,更緊湊。 但是string key有關鍵的名字信息,對調試很有用。
MessagePackSerializer序列化目標時,必須在目標使用特性才能保證穩健性,如果類進行了擴充,你必須意識到版本控制。如果Key不存在,MessagePackSerializer將會使用默認值。如果使用的是int key,那麽必須從0開始,如果不必要的屬性出現,請填寫空缺的數字。重用是不好的。 此外,如果Int Key的跳轉數字差距太大,則會影響二進制大小。
[MessagePackObject]
public class IntKeySample
{
[Key(3)]
public int A { get; set; }
[Key(10)]
public int B { get; set; }
}
// int key不從0開始並且數字進行了跳躍,將會出現下面的結果
//[null,null,null,0,null,null,null,null,null,null,0]
Console.WriteLine(MessagePackSerializer.ToJson(new IntKeySample()));
如果你想像JSON.NET那樣使用!不想加特性! 如果你這樣想,你可以使用無約定的解析器。
public class ContractlessSample
{
public int MyProperty1 { get; set; }
public int MyProperty2 { get; set; }
}
var data = new ContractlessSample { MyProperty1 = 99, MyProperty2 = 9999 };
var bin = MessagePackSerializer.Serialize(data, MessagePack.Resolvers.ContractlessStandardResolver.Instance);
// {"MyProperty1":99,"MyProperty2":9999}
Console.WriteLine(MessagePackSerializer.ToJson(bin));
// 全局設置無約束解析器為默認解析器
MessagePackSerializer.SetDefaultResolver(MessagePack.Resolvers.ContractlessStandardResolver.Instance);
// 序列化
var bin2 = MessagePackSerializer.Serialize(data);
我想序列化私人成員! 默認情況下,不能序列化/反序列化私有成員。 但是你可以使用allow-private解析器來序列化私人成員。
[MessagePackObject]
public class PrivateSample
{
[Key(0)]
int x;
public void SetX(int v)
{
x = v;
}
public int GetX()
{
return x;
}
}
var data = new PrivateSample();
data.SetX(9999);
// 你可以選擇 StandardResolverAllowPrivate 或者 ContractlessStandardResolverAllowPrivate 解析器
var bin = MessagePackSerializer.Serialize(data, MessagePack.Resolvers.DynamicObjectResolverAllowPrivate.Instance);
我不需要類型,我想像BinaryFormatter那樣使用! 你可以使用無類型的解析器和幫助器。 請參閱Typeless部分。
解析器是MessagePack For C#的關鍵定制點。 詳情請見擴展部分。
DataContract兼容性
您可以使用[DataContract]而不是[MessagePackObject]。 如果type標記為DataContract,則可以使用[DataMember]代替[Key],[IgnoreDataMember]代替[IgnoreMember]。
[DataMember(Order = int)] 和 [Key(int)]相同, [DataMember(Name = string)]和 [Key(string)]相同. 如果使用 [DataMember], 則類似於 [Key(nameof(propertyname)].
使用DataContract使其成為一個共享的類庫,您不必引用MessagePack for C#。 但是,它不包含在分析器或由mpc.exe
生成的代碼中。此外,像UnionAttribute,MessagePackFormatterAttribute,SerializationConstructorAttribute等功能不能使用。 出於這個原因,我建議您基本上使用MessagePack for C#特性。
序列化不可變對象(序列化構造器)
MessagePack for C#支持反序列化不可變對象。 例如,這個struct可以自然地序列化/反序列化。
[MessagePackObject]
public struct Point
{
[Key(0)]
public readonly int X;
[Key(1)]
public readonly int Y;
public Point(int x, int y)
{
this.X = x;
this.Y = y;
}
}
var data = new Point(99, 9999);
var bin = MessagePackSerializer.Serialize(data);
// Okay to deserialize immutable obejct
var point = MessagePackSerializer.Deserialize<Point>(bin);
MessagePackSerializer choose constructor with the least matched argument, match index if key in integer or match name(ignore case) if key is string. If encounts MessagePackDynamicObjectResolverException: can‘t find matched constructor parameter you should check about this.
MessagePackSerializer選擇具有最少參數的構造方法,如果key是整型將匹配索引或者如果key是字符串將匹配名稱(忽略大小寫)。 如果遇到 MessagePackDynamicObjectResolverException: can‘t find matched constructor parameter
你應該檢查一會下。
如果不能自動匹配,可以通過[SerializationConstructorAttribute]手動指定使用構造函數。
[MessagePackObject]
public struct Point
{
[Key(0)]
public readonly int X;
[Key(1)]
public readonly int Y;
// 如果沒有標記特性,將會使用這方法(最少參數)
public Point(int x)
{
X = x;
}
[SerializationConstructor]
public Point(int x, int y)
{
this.X = x;
this.Y = y;
}
}
序列化回調
如果對象實現了IMessagePackSerializationCallbackReceiver
,則接受OnBeforeSerialize
和OnAfterDeserialize
序列化處理。
[MessagePackObject]
public class SampleCallback : IMessagePackSerializationCallbackReceiver
{
[Key(0)]
public int Key { get; set; }
public void OnBeforeSerialize()
{
Console.WriteLine("OnBefore");
}
public void OnAfterDeserialize()
{
Console.WriteLine("OnAfter");
}
}
Union
MessagePack for C#支持序列化接口。這就像XmlInclude或ProtoInclude。在MessagePack for C#裏叫Union。UnionAttribute只能附加到接口或抽象類。 它需要區分的整型key和子類型
// mark inheritance types
[MessagePack.Union(0, typeof(FooClass))]
[MessagePack.Union(1, typeof(BarClass))]
public interface IUnionSample
{
}
[MessagePackObject]
public class FooClass : IUnionSample
{
[Key(0)]
public int XYZ { get; set; }
}
[MessagePackObject]
public class BarClass : IUnionSample
{
[Key(0)]
public string OPQ { get; set; }
}
// ---
IUnionSample data = new FooClass() { XYZ = 999 };
// serialize interface.
var bin = MessagePackSerializer.Serialize(data);
// deserialize interface.
var reData = MessagePackSerializer.Deserialize<IUnionSample>(bin);
// use type-switch of C# 7.0
switch (reData)
{
case FooClass x:
Console.WriteLine(x.XYZ);
break;
case BarClass x:
Console.WriteLine(x.OPQ);
break;
default:
break;
}
C#7.0 type-switch是Union的最佳選擇。 Union被序列化為兩個長度的數組。
IUnionSample data = new BarClass { OPQ = "FooBar" };
var bin = MessagePackSerializer.Serialize(data);
// Union is serialized to two-length array, [key, object]
// [1,["FooBar"]]
Console.WriteLine(MessagePackSerializer.ToJson(bin));
在抽象類中使用Union,你可以像接口那樣使用。
[Union(0, typeof(SubUnionType1))]
[Union(1, typeof(SubUnionType2))]
[MessagePackObject]
public abstract class ParentUnionType
{
[Key(0)]
public int MyProperty { get; set; }
}
[MessagePackObject]
public class SubUnionType1 : ParentUnionType
{
[Key(1)]
public int MyProperty1 { get; set; }
}
[MessagePackObject]
public class SubUnionType2 : ParentUnionType
{
[Key(1)]
public int MyProperty2 { get; set; }
}
繼承類型的序列化,在數組(或鍵值對)中是扁平化的,對於整型鍵是無關緊要的,它不能復制父類和所有的子類。
Dynamic(Untyped)反序列化
如果使用MessagePackSerializer.Deserialize<object>
或者MessagePackSerializer.Deserialize<dynamic>
,messagepack將轉換為 primitive values,msgpack-primitive將轉換為bool, char, sbyte, byte, short, int, long, ushort, uint, ulong, float, double, DateTime, string, byte[], object[], IDictionary<object, object>
.
// sample binary.
var model = new DynamicModel { Name = "foobar", Items = new[] { 1, 10, 100, 1000 } };
var bin = MessagePackSerializer.Serialize(model, ContractlessStandardResolver.Instance);
// dynamic, untyped
var dynamicModel = MessagePackSerializer.Deserialize<dynamic>(bin, ContractlessStandardResolver.Instance);
Console.WriteLine(dynamicModel["Name"]); // foobar
Console.WriteLine(dynamicModel["Items"][2]); // 100
所以你可以使用索引訪問鍵值對或者數組。
Object 類型序列化
StandardResolver
和ContractlessStandardResolver
可以通過DynamicObjectTypeFallbackResolver
將Object類型序列化為具體類型。
var objects = new object[] { 1, "aaa", new ObjectFieldType { Anything = 9999 } };
var bin = MessagePackSerializer.Serialize(objects);
// [1,"aaa",[9999]]
Console.WriteLine(MessagePackSerializer.ToJson(bin));
// Support Anonymous Type Serialize
var anonType = new { Foo = 100, Bar = "foobar" };
var bin2 = MessagePackSerializer.Serialize(anonType, MessagePack.Resolvers.ContractlessStandardResolver.Instance);
// {"Foo":100,"Bar":"foobar"}
Console.WriteLine(MessagePackSerializer.ToJson(bin2));
Unity支持是有限的。
反序列化時,與Dynamic(Untyped)反序列化相同。
Typeless
Typeless API就像BinaryFormatter, 將類型信息嵌入到二進制中,所以不需要類型去反序列化.
object mc = new Sandbox.MyClass()
{
Age = 10,
FirstName = "hoge",
LastName = "huga"
};
// serialize to typeless
var bin = MessagePackSerializer.Typeless.Serialize(mc);
// binary data is embeded type-assembly information.
// ["Sandbox.MyClass, Sandbox",10,"hoge","huga"]
Console.WriteLine(MessagePackSerializer.ToJson(bin));
// can deserialize to MyClass with typeless
var objModel = MessagePackSerializer.Typeless.Deserialize(bin) as MyClass;
類型信息由mspgack ext格式序列化,typecode為100。
MessagePackSerializer.Typeless
是Serialize / Deserialize <object>(TypelessContractlessStandardResolver.Instance)
的快捷方式。 如果要配置默認的Typeless解析器,可以通過MessagePackSerializer.Typeless.RegisterDefaultResolver
進行設置。
性能
與其他序列化器在Windows 10 Pro x64 Intel Core i7-6700K 4.00GHz, 32GB RAM上進行Benchmarks比較,Benchmark代碼在這-版本信息,ZeroFormatter和FlatBuffers具有非常快速的反序列化器,因此忽略反序列化的性能。
MessagePack for C#使用許多技術來提高性能。
- 序列化只使用ref byte []和int offset,不使用(Memory)Stream(調用Stream api會有開銷)
- 高級API使用內部內存池,分配工作內存不要低於64k
- 不創建中間實用程序實例(XxxWriter / Reader,XxxContext等)
- 所有代碼避免裝箱,所有平臺(包括Unity / IL2CPP)
- 對靜態泛型字段生成的格式化程序進行緩存,查找時從緩存查找(不使用字典緩存,因為字典查找需要一定開銷)
- 重新調整的動態代碼生成
- 當代碼生成知道目標是primitive時直接調用PrimitiveAPI
- 當代碼生成知道目標(整數/字符串)範圍時,減少可變長度格式的分支
- 不在叠代集合上使用IEnumerable<T> 抽象
- 使用預先生成的查找表來減少檢查消息包類型所耗時間
- 對非泛型方法使用優化類型key字典
- 避免查找映射(字符串鍵)鍵的字符串鍵解碼,並使用自動化名稱查找與il內聯代碼生成
- 對於字符串鍵編碼,預先生成的成員名字節並在IL中使用固定大小的二進制副本
在創建這個庫之前,作則實現了一個具有ZeroFormatter#Performance的快速序列化器。 這是一個進一步演變的實現。 MessagePack for C#始終是快速的,為所有類型(原始,小結構,大對象,任何集合)進行了優化。
反序列化中每個方法的性能
性能取決於選項。 這是一個BenchmarkDotNet的微型benchamark。 目標對象有9個成員(MyProperty1?MyProperty9),值為零。
Method | Mean | Error | Scaled | Gen 0 | Allocated |
---|---|---|---|---|---|
IntKey | 72.67 ns | NA | 1.00 | 0.0132 | 56 B |
StringKey | 217.95 ns | NA | 3.00 | 0.0131 | 56 B |
Typeless_IntKey | 176.71 ns | NA | 2.43 | 0.0131 | 56 B |
Typeless_StringKey | 378.64 ns | NA | 5.21 | 0.0129 | 56 B |
MsgPackCliMap | 1,355.26 ns | NA | 18.65 | 0.1431 | 608 B |
MsgPackCliArray | 455.28 ns | NA | 6.26 | 0.0415 | 176 B |
ProtobufNet | 265.85 ns | NA | 3.66 | 0.0319 | 136 B |
Hyperion | 366.47 ns | NA | 5.04 | 0.0949 | 400 B |
JsonNetString | 2,783.39 ns | NA | 38.30 | 0.6790 | 2864 B |
JsonNetStreamReader | 3,297.90 ns | NA | 45.38 | 1.4267 | 6000 B |
JilString | 553.65 ns | NA | 7.62 | 0.0362 | 152 B |
JilStreamReader | 1,408.46 ns | NA | 19.38 | 0.8450 | 3552 B |
IntKey,StringKey,Typeless_IntKey,Typeless_StringKey都是MessagePack for C#的方法
,在反序列化過程中實現零內存分配。JsonNetString /JilString從字符串反序列化。JsonStStreamReader / JilStreamReader是從StreamReader的UTF8 byte []中反序列化的。反序列化通常從Stream讀取。 因此,它將從字節數組(或流)而不是字符串中讀取。
MessagePack for C#IntKey是最快的。 StringKey比IntKey慢,因為StringKey需要從字符串進行匹配。 如果是IntKey,讀取數組長度,根據數組長度進行for循環二進制解碼。 如果StringKey,讀取map 長度,根據map長度循環,首先需要對密鑰解碼,然後按照key查找,最後二進制解碼,則需要額外兩個步驟(解碼密鑰和按鍵查找)。
字符串鍵通常是有用的,無約束的,簡單的JSON替換,與其他語言的互操作性,以及更多的某些版本。 MessagePack for C#也為String Key進行了優化。 首先,它不會將UTF8字節數組解碼為與成員名稱匹配的字符串,它會按原樣查找字節數組(避免解碼成本和額外分配)。
它會嘗試匹配每個長整型(long)(每8個字符,如果長度不夠,填充0)使用automata和在生成時內聯IL代碼。
這也避免了計算字節數組的哈希碼,並且可以在長單元上進行多次比較。
這是ILSpy生成的反序列化器代碼的示例的反編譯。
https://github.com/neuecc/MessagePack-CSharp#performance
如果節點數量很大,則使用嵌入式二進制搜索進行搜索。
另外請註意,這是序列化的基準測試結果。
Method | Mean | Error | Scaled | Gen 0 | Allocated |
---|---|---|---|---|---|
IntKey | 84.11 ns | NA | 1.00 | 0.0094 | 40 B |
StringKey | 126.75 ns | NA | 1.51 | 0.0341 | 144 B |
Typeless_IntKey | 183.31 ns | NA | 2.18 | 0.0265 | 112 B |
Typeless_StringKey | 193.95 ns | NA | 2.31 | 0.0513 | 216 B |
MsgPackCliMap | 967.68 ns | NA | 11.51 | 0.1297 | 552 B |
MsgPackCliArray | 284.20 ns | NA | 3.38 | 0.1006 | 424 B |
ProtobufNet | 176.43 ns | NA | 2.10 | 0.0665 | 280 B |
Hyperion | 280.14 ns | NA | 3.33 | 0.1674 | 704 B |
ZeroFormatter | 149.95 ns | NA | 1.78 | 0.1009 | 424 B |
JsonNetString | 1,432.55 ns | NA | 17.03 | 0.4616 | 1944 B |
JsonNetStreamWriter | 1,775.72 ns | NA | 21.11 | 1.5526 | 6522 B |
JilString | 547.51 ns | NA | 6.51 | 0.3481 | 1464 B |
JilStreamWriter | 778.78 ns | NA | 9.26 | 1.4448 | 6066 B |
當然,IntKey是最快的,但StringKey也不錯。
LZ4壓縮
MessagePack是一個快速和緊湊的格式,但它不是壓縮格式。 LZ4是非常快速的壓縮算法,使用MessagePack for C#可以實現極快的性能和非常緊湊的二進制大小!
MessagePack for C#具有內置的LZ4支持。 您可以使用LZ4MessagePackSerializer而不是MessagePackSerializer。 內建支持是特殊的,作者已經創建了序列化壓縮管道,並專門調整了管道,所以共享工作內存,不分配,不要調整,直到完成。
序列化二進制不是簡單地壓縮lz4二進制。 序列化二進制是有效的MessagePack二進制使用ext格式和自定義typecode(99)。
var array= Enumerable.Range(1, 100).Select(x => new MyClass { Age = 5, FirstName = "foo", LastName = "bar" }).ToArray();
// call LZ4MessagePackSerializer instead of MessagePackSerializer, api is completely same
var lz4Bytes = LZ4MessagePackSerializer.Serialize(array);
var mc2 = LZ4MessagePackSerializer.Deserialize<MyClass[]>(lz4Bytes);
// you can dump lz4 message pack
// [[5,"hoge","huga"],[5,"hoge","huga"],....]
var json = LZ4MessagePackSerializer.ToJson(lz4Bytes);
Console.WriteLine(json);
// lz4Bytes is valid MessagePack, it is using ext-format( [TypeCode:99, SourceLength|CompressedBinary] )
// [99,"0gAAA+vf3ABkkwWjZm9vo2JhcgoA////yVBvo2Jhcg=="]
var rawJson = MessagePackSerializer.ToJson(lz4Bytes);
Console.WriteLine(rawJson);
與protobuf,JSON,ZeroFormatter比較
protbuf-net是.NET上最常用的二進制格式化庫。 我(作者)喜歡protobuf-net,並尊重那偉大的工作。 但是如果使用protobuf-net作為通用序列化格式,則可能會引起煩人的問題。
[ProtoContract]
public class Parent
{
[ProtoMember(1)]
public int Primitive { get; set; }
[ProtoMember(2)]
public Child Prop { get; set; }
[ProtoMember(3)]
public int[] Array { get; set; }
}
[ProtoContract]
public class Child
{
[ProtoMember(1)]
public int Number { get; set; }
}
using (var ms = new MemoryStream())
{
// serialize null.
ProtoBuf.Serializer.Serialize<Parent>(ms, null);
ms.Position = 0;
var result = ProtoBuf.Serializer.Deserialize<Parent>(ms);
Console.WriteLine(result != null); // True, not null. but all property are zero formatted.
Console.WriteLine(result.Primitive); // 0
Console.WriteLine(result.Prop); // null
Console.WriteLine(result.Array); // null
}
using (var ms = new MemoryStream())
{
// serialize empty array.
ProtoBuf.Serializer.Serialize<Parent>(ms, new Parent { Array = new int[0] });
ms.Position = 0;
var result = ProtoBuf.Serializer.Deserialize<Parent>(ms);
Console.WriteLine(result.Array == null); // True, null!
}
protobuf(-net)不能正確處理null和空集合。 因為protobuf沒有null表示(這是protobuf-net作者的答案)。
MessagePack規範可以完全序列化C#類型。 這就是推薦MessagePack而不是protobuf的原因。
Protocol Buffers具有良好的IDL和gRPC,這比MessagePack好得多。 如果你想使用IDL,我(作者)推薦Google.Protobuf。
JSON是很好的通用格式。 這是完美的,簡單的,足夠規範的。 Utf8Json創建了我采用與MessagePack for C#相同的體系結構,並避免編碼/修飾成本,所以像二進制一樣工作。 如果你想了解二進制與文本,請參閱Utf8Json /應使用哪個序列化器部分。
ZeroFormatter與FlatBuffers類似,但專門用於C#。 這是特別的。 反序列化速度非常快,但是二進制大小卻很大。 而ZeroFormatter的緩存算法需要額外的內存。
ZeroFormatter也是特別的。 當與ZeroFormatter對比的情況下,它顯示格式化的力量。 但是對於許多常見的用途,MessagePack for C#會更好。
擴展
MessagePack for C#具有擴展點,您可以添加外部類型的序列化支持。 下列是官方擴展支持。
Install-Package MessagePack.ImmutableCollection
Install-Package MessagePack.ReactiveProperty
Install-Package MessagePack.UnityShims
Install-Package MessagePack.AspNetCoreMvcFormatter
MessagePack.ImmutableCollection
添加對 System.Collections.Immutable
的支持. 添加了對ImmutableArray<>, ImmutableList<>, ImmutableDictionary<,>, ImmutableHashSet<>, ImmutableSortedDictionary<,>, ImmutableSortedSet<>, ImmutableQueue<>, ImmutableStack<>, IImmutableList<>, IImmutableDictionary<,>, IImmutableQueue<>, IImmutableSet<>, IImmutableStack<>
的序列化支持.
MessagePack.ReactiveProperty包添加對ReactiveProperty庫的支持。它增加了ReactiveProperty <>,IReactiveProperty <>,IReadOnlyReactiveProperty <>,ReactiveCollection <>,unit
序列化支持。 這對保存視圖模型狀態很有用。
MessagePack.AspNetCoreMvcFormatter是ASP.NET Core MVC序列化的附加組件,可提升性能。 這是配置示例。
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().AddMvcOptions(option =>
{
option.OutputFormatters.Clear();
option.OutputFormatters.Add(new MessagePackOutputFormatter(ContractlessStandardResolver.Instance));
option.InputFormatters.Clear();
option.InputFormatters.Add(new MessagePackInputFormatter(ContractlessStandardResolver.Instance));
});
}
更多信息請訪問github: https://github.com/neuecc/MessagePack-CSharp
快速序列化組件MessagePack介紹