快速序列化组件MessagePack介绍 简介 使用 快速开始 分析器 内置的支持类型 对象序列化 DataContract兼容性 序列化不可变对象(序列化构造器) 序列化回调 Union Dynamic(Untyped)反序列化 Object 类型序列化 Typeless 性能 反序列化中每个方法的性能 LZ4压缩 与protobuf,JSON,ZeroFormatter比较 扩展

MessagePack for C#(MessagePack-CSharp)是用于C#的极速MessagePack序列化程序,比MsgPack-Cli快10倍,与其他所有C#序列化程序相比,具有最好的性能。 MessagePack for C#具有内置的LZ4压缩功能,可以实现超快速序列化和二进制占用空间小。 性能永远是重要的! 可用于游戏,分布式计算,微服务,数据存储到Redis等。支持.NET, .NET Core, Unity, Xamarin。

快速序列化组件MessagePack介绍
简介
使用
快速开始
分析器
内置的支持类型
对象序列化
DataContract兼容性
序列化不可变对象(序列化构造器)
序列化回调
Union
Dynamic(Untyped)反序列化
Object 类型序列化
Typeless
性能
反序列化中每个方法的性能
LZ4压缩
与protobuf,JSON,ZeroFormatter比较
扩展

从上图我们看出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 可以帮助我们定义对象. 如果不符合规则,那么特性, 程序集等可以被检测到,如果我们编译就会出现编译错误。

快速序列化组件MessagePack介绍
简介
使用
快速开始
分析器
内置的支持类型
对象序列化
DataContract兼容性
序列化不可变对象(序列化构造器)
序列化回调
Union
Dynamic(Untyped)反序列化
Object 类型序列化
Typeless
性能
反序列化中每个方法的性能
LZ4压缩
与protobuf,JSON,ZeroFormatter比较
扩展

如果要允许特定类型(例如,注册自定义类型时),请将MessagePackAnalyzer.json放在项目根目录下,并将生成操作设置为AdditionalFiles(其他文件)。

快速序列化组件MessagePack介绍
简介
使用
快速开始
分析器
内置的支持类型
对象序列化
DataContract兼容性
序列化不可变对象(序列化构造器)
序列化回调
Union
Dynamic(Untyped)反序列化
Object 类型序列化
Typeless
性能
反序列化中每个方法的性能
LZ4压缩
与protobuf,JSON,ZeroFormatter比较
扩展

这是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,则接受OnBeforeSerializeOnAfterDeserialize序列化处理。

[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 类型序列化

StandardResolverContractlessStandardResolver可以通过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.TypelessSerialize / 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介绍
简介
使用
快速开始
分析器
内置的支持类型
对象序列化
DataContract兼容性
序列化不可变对象(序列化构造器)
序列化回调
Union
Dynamic(Untyped)反序列化
Object 类型序列化
Typeless
性能
反序列化中每个方法的性能
LZ4压缩
与protobuf,JSON,ZeroFormatter比较
扩展

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代码。

快速序列化组件MessagePack介绍
简介
使用
快速开始
分析器
内置的支持类型
对象序列化
DataContract兼容性
序列化不可变对象(序列化构造器)
序列化回调
Union
Dynamic(Untyped)反序列化
Object 类型序列化
Typeless
性能
反序列化中每个方法的性能
LZ4压缩
与protobuf,JSON,ZeroFormatter比较
扩展

这也避免了计算字节数组的哈希码,并且可以在长单元上进行多次比较。

这是ILSpy生成的反序列化器代码的示例的反编译。

快速序列化组件MessagePack介绍
简介
使用
快速开始
分析器
内置的支持类型
对象序列化
DataContract兼容性
序列化不可变对象(序列化构造器)
序列化回调
Union
Dynamic(Untyped)反序列化
Object 类型序列化
Typeless
性能
反序列化中每个方法的性能
LZ4压缩
与protobuf,JSON,ZeroFormatter比较
扩展

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