MessagePack-CSharp
Serialization Library
MessagePack-CSharp
Overview
MessagePack-CSharp is an extremely fast MessagePack serializer for C#/.NET developed by neuecc. It achieves over 10x faster performance than JSON and significantly reduces data size through binary serialization. Targeting .NET Standard 2.0, it supports a wide range of platforms including Unity, Xamarin, .NET Core, and .NET Framework.
Details
MessagePack-CSharp is a performance-first serializer that employs optimization techniques such as zero-allocation deserialization, dynamic code generation, and buffer pooling. It supports compile-time code generation through Source Generators, enabling high-speed operation even in AOT environments.
Key Features:
- Ultra-high Performance: 10x faster than MsgPack-Cli, 17x+ faster than JSON.NET
- Compact Binary Size: 50-70% size reduction compared to JSON
- Full Unity Support: Built-in support for Unity standard types (Vector3, Quaternion, etc.)
- Source Generator Support: AOT compatibility and compile-time validation
- LZ4 Compression Support: Further size reduction through fast compression
- Union Type Support: Polymorphic serialization capabilities
- Security Features: Safe deserialization of untrusted data
Architecture:
- Fastest serialization with indexed keys (IntKey)
- Readable debugging with string keys (StringKey)
- Contractless mode for attribute-free serialization
- Extensibility through custom formatters
Pros and Cons
Pros
- Outstanding Performance: One of the fastest serializers in the C# ecosystem
- Memory Efficiency: Zero-allocation design minimizes GC pressure
- Unity Integration: Proven track record in game development with Unity-specific optimizations
- Type Safety: Compile-time validation through Source Generators
- Flexible Configuration: Three serialization modes for different use cases
- Compression Support: Network transfer optimization with LZ4 fast compression
- Active Development: Continuous maintenance by Cysharp
Cons
- Binary Format: Human-unreadable format makes debugging difficult
- Learning Curve: Understanding required for advanced features
- Attribute-based: MessagePackObject attributes typically required
- Versioning: Careful management needed for schema compatibility
Reference Pages
Code Examples
Basic Serialization
// 1. Using indexed keys (fastest)
[MessagePackObject]
public class Person
{
[Key(0)]
public int Id { get; set; }
[Key(1)]
public string Name { get; set; }
[Key(2)]
public int Age { get; set; }
[IgnoreMember]
public string IgnoredProperty { get; set; }
}
// Serialization
var person = new Person { Id = 1, Name = "John", Age = 30 };
byte[] bytes = MessagePackSerializer.Serialize(person);
// Deserialization
Person deserialized = MessagePackSerializer.Deserialize<Person>(bytes);
// Verify in JSON format
string json = MessagePackSerializer.ConvertToJson(bytes);
Console.WriteLine(json); // [1,"John",30]
String Keys and Contractless Mode
// 2. String keys (easier debugging)
[MessagePackObject]
public class Product
{
[Key("product_id")]
public int ProductId { get; set; }
[Key("name")]
public string Name { get; set; }
[Key("price")]
public decimal Price { get; set; }
}
// 3. Contractless mode (no attributes required)
public class Order
{
public int OrderId { get; set; }
public DateTime OrderDate { get; set; }
public List<Product> Items { get; set; }
}
// Contractless mode serialization
var options = MessagePack.Resolvers.ContractlessStandardResolver.Options;
var order = new Order
{
OrderId = 1001,
OrderDate = DateTime.Now,
Items = new List<Product> { /* ... */ }
};
byte[] bytes = MessagePackSerializer.Serialize(order, options);
Order deserialized = MessagePackSerializer.Deserialize<Order>(bytes, options);
Unity Optimization
// Unity initialization
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
private static void InitializeMessagePack()
{
// Enable Unity type support
var options = MessagePackSerializerOptions.Standard
.WithResolver(MessagePack.Unity.UnityResolver.Instance);
MessagePackSerializer.DefaultOptions = options;
}
// Unity type serialization
[MessagePackObject]
public class GameData
{
[Key(0)]
public Vector3 Position { get; set; }
[Key(1)]
public Quaternion Rotation { get; set; }
[Key(2)]
public Color Color { get; set; }
}
// High-speed struct array serialization (Unsafe)
StaticCompositeResolver.Instance.Register(
MessagePack.Unity.UnityResolver.Instance,
MessagePack.Unity.Extension.UnityBlitWithPrimitiveArrayResolver.Instance,
MessagePack.Resolvers.StandardResolver.Instance
);
// Optimal for large data like mesh data
Vector3[] vertices = mesh.vertices;
byte[] bytes = MessagePackSerializer.Serialize(vertices);
LZ4 Compression and Security Settings
// Enable LZ4 compression
var lz4Options = MessagePackSerializerOptions.Standard
.WithCompression(MessagePackCompression.Lz4BlockArray);
byte[] compressed = MessagePackSerializer.Serialize(data, lz4Options);
var decompressed = MessagePackSerializer.Deserialize<MyData>(compressed, lz4Options);
// Handle untrusted data
var secureOptions = MessagePackSerializerOptions.Standard
.WithSecurity(MessagePackSecurity.UntrustedData);
try
{
var result = MessagePackSerializer.Deserialize<MyData>(untrustedData, secureOptions);
}
catch (MessagePackSerializationException ex)
{
// Detect security violations
Console.WriteLine($"Security error: {ex.Message}");
}
Source Generator and AOT Support
// Attributes for Source Generator
[GeneratedMessagePackResolver]
[MessagePackObject]
public partial class MyModel
{
[Key(0)]
public int Id { get; set; }
[Key(1)]
public string Name { get; set; }
}
// Enable Source Generator in project file
// <PackageReference Include="MessagePack.Generator" Version="2.5.*" />
// Use generated resolver
var options = MessagePackSerializerOptions.Standard
.WithResolver(GeneratedResolver.Instance);
// High-speed operation even in AOT environments like Unity IL2CPP
MessagePackSerializer.DefaultOptions = options;
Custom Formatter
// Custom formatter to save DateTime as Unix time
public class UnixTimeDateTimeFormatter : IMessagePackFormatter<DateTime>
{
public void Serialize(ref MessagePackWriter writer, DateTime value,
MessagePackSerializerOptions options)
{
var unixTime = ((DateTimeOffset)value).ToUnixTimeSeconds();
writer.Write(unixTime);
}
public DateTime Deserialize(ref MessagePackReader reader,
MessagePackSerializerOptions options)
{
var unixTime = reader.ReadInt64();
return DateTimeOffset.FromUnixTimeSeconds(unixTime).DateTime;
}
}
// Register custom formatter
var resolver = CompositeResolver.Create(
new IMessagePackFormatter[] { new UnixTimeDateTimeFormatter() },
new IFormatterResolver[] { StandardResolver.Instance }
);
var options = MessagePackSerializerOptions.Standard.WithResolver(resolver);
Union Types (Polymorphism)
[Union(0, typeof(Cat))]
[Union(1, typeof(Dog))]
public abstract class Animal
{
public abstract string MakeSound();
}
[MessagePackObject]
public class Cat : Animal
{
[Key(0)]
public string Name { get; set; }
public override string MakeSound() => "Meow";
}
[MessagePackObject]
public class Dog : Animal
{
[Key(0)]
public string Name { get; set; }
[Key(1)]
public string Breed { get; set; }
public override string MakeSound() => "Woof";
}
// Polymorphic serialization
Animal[] animals = new Animal[]
{
new Cat { Name = "Whiskers" },
new Dog { Name = "Buddy", Breed = "Golden Retriever" }
};
byte[] bytes = MessagePackSerializer.Serialize(animals);
Animal[] deserialized = MessagePackSerializer.Deserialize<Animal[]>(bytes);