CLI tool
MessagePack-CSharp/MessagePack-CSharp avatar
MessagePack-CSharp/MessagePack-CSharp

MessagePack for C#: binary serialization that treats the wire format as a design decision

Extremely Fast MessagePack Serializer for C#(.NET, .NET Core, Unity, Xamarin). / msgpack.org[C#]

6,787 stars775 forksC#NOASSERTION

At a glance

What is it?
6,786 stars, a .NET Foundation project, and a README that spends more words on performance hints than on installation. The interesting part is the model: you assign integer keys to members and the serializer never looks at a property name again.
Who is it for?
MessagePack for C# is the rare serialization library where the README's length is justified by the surface area. There are two APIs for every operation, three resolver strategies, a source generator for ahead-of-time compilation, a disallow list for named-type deserialization, and a migration guide for each major version.
Can I use it commercially?
Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
Is it still maintained?
Yes. The repository last received commits 1 day ago.
What is it written in?
Mainly C#, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 7, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Three packages, one install line

The distribution story is short, because there is only one package you need for most projects.

ps1
Install-Package MessagePack

The target is .NET Standard 2.0 with special optimisations for .NET 8+ and .NET Framework, which means one binary works across desktop, server and modern framework versions, and the library gets faster on newer runtimes without you changing anything. The implementation is pure C#, with just-in-time IL generation on some platforms and ahead-of-time-safe source generators as the alternative. That phrasing covers the two ways this library reaches compiled form, which matters because the two have very different performance and startup characteristics.

Three extension packages are listed alongside it, and the naming tells you what each one integrates with: `MessagePack.ReactiveProperty` for that reactive property library, `MessagePack.UnityShims` for Unity's older .NET profile, and `MessagePack.AspNetCoreMvcFormatter` for ASP.NET Core MVC. Official and third-party extensions exist beyond those three, and the README links to its own extensions section rather than enumerating them all.

Integer keys are the whole contract

The quick start starts with an attribute on the class and an integer on every member you want serialized.

csharp
[MessagePackObject]
public class MyClass
{
    [Key(0)]
    public int Age { get; set; }

    [Key(1)]
    public string FirstName { get; set; }

    [Key(2)]
    public string LastName { get; set; }

    [IgnoreMember]
    public string FullName { get { return FirstName + LastName; } }
}

The comment on the first key is the design brief: keys take a serialization index or a string name, the values must be unique, and versioning has to be considered. Everything about this library follows from that. An integer key becomes one byte on the wire instead of a property name, and the serializer can emit the shape without reflecting over names at runtime. It also means renumbering a key is a breaking change to a persisted format, which is why the versioning note sits in the first code sample rather than in a later section.

`[IgnoreMember]` marks anything that should not travel, and in the example that is a computed property. There is a string-key alternative for cases where you want readable output without maintaining a numbering scheme, and the README's performance hints section explicitly recommends indexed keys over string keys as the faster option, which tells you the choice is available rather than mandated.

Members can also be readonly, with a serialization constructor for immutable types, and DataContract compatibility exists if you are migrating from another serializer. Serialization callbacks are documented for the cases where the generated constructor is not enough.

Three layers of API, and which one you should be using

The README documents three levels of API, and the hierarchy matters because picking the wrong one is how projects end up slow.

The high-level API is `MessagePackSerializer`, with `Serialize` and `Deserialize` generic methods and a `ConvertToJson` helper that turns any MessagePack blob into readable JSON for inspection. That helper is more useful than it sounds, because it is the fastest way to see what a payload actually contains without writing a deserializer.

Below that is the low-level `IMessagePackFormatter<T>` interface, which is what the source generator emits for your types and what you implement yourself when you want control. Below that again is the primitive API, `MessagePackWriter` and `MessagePackReader`, which operate on the binary format directly and carry their own dedicated documentation sections. Writing a custom formatter by hand is reasonable for hot types; writing a reader loop by hand is rarely necessary.

The extension point that connects the layers is `IFormatterResolver`, and the README's hints section has specific advice about it: build your own custom composite resolver when you want control over which resolvers are used and in what order, rather than relying on the default.

It also recommends using native resolvers where they exist, being careful when copying buffers, and choosing compression deliberately. That last point links to the LZ4 section, and the recommendation is not simply always-on.

Performance claims, and where they come from

The README opens with a comparison: 10x faster than MsgPack-Cli, and better than other C# serializers. It also states that performance matters particularly in games, distributed computing, microservices and data caches. That is a project claim rather than an independent measurement, and the repository includes a `benchmark/` directory where the comparison work lives.

The mechanism behind the claim is the key model, and the README's own hints section is the honest part. Prefer indexed keys over string keys, because every string key is bytes on the wire and work at both ends. Build a composite resolver so hot types get a precompiled formatter. Use the native resolvers for primitive types rather than the general path. Be careful when copying buffers, since avoiding a copy is worth more than most micro-optimisations.

There is also a documented performance dimension that has nothing to do with encoding: deserialization performance varies with options, and there is a section specifically on that, plus one on string interning. String interning matters when you deserialize the same strings repeatedly, which happens constantly in cache and message scenarios.

The comparison section covers protobuf, JSON and ZeroFormatter, and if you are choosing a serialization format rather than just using one, that section is the part of this README that answers your question.

Ahead-of-time compilation and the Unity constraint

The library has an explicit AOT story, documented under a heading that names the platforms directly: AOT code generation for Unity and Xamarin. That matters because these platforms compile ahead of time and cannot emit IL at runtime, so a serializer that relies on dynamic codegen simply does not work there. The source generator path exists to solve exactly that problem, and the README's separate Unity support section covers installation specifics.

Alongside AOT, there is an Analyzer, documented as its own section near the top of the table of contents. A Roslyn analyzer is a compile-time addition, and having one for a serialization library means mistakes surface at build time rather than as a runtime exception: a missing key index, a duplicate index, or a type the generator cannot handle becomes a build warning instead of a bug.

There is also dynamic, untyped deserialization, object type serialization, and typeless mode, which is the option to be most careful with because it writes type information into the payload. The built-in supported types list has its own section, and the most recent release added missing types to that list, which tells you the surface is still growing.

Deserialization is the attack surface, and the project treats it that way

There is a Security section, and recent releases show what it contains in practice.

The v3.1.9 release fixes a security advisory titled nested arrays can blow past buffer length limits. The same day, a separate v2.5.303 release carries the same advisory fix, because the vulnerability existed on both maintained lines. That is worth noting as a process signal: a security fix on two branches at once, rather than a backport you might never receive.

The same release also adds more types to the default disallow list of named types to be deserialized, and several known unsafe gadgets to that list. That is the deserialization-gadget problem, and it is the reason typeless mode deserves caution. If a payload can name the type it wants constructed, an attacker can sometimes name a type that does something dangerous during construction. The disallow list is the mitigation, and it has been actively extended.

Other changes in that release cluster are also instructive: honouring different AssemblyLoadContexts, which matters in plugin architectures, a fix for old-spec byte array encoding emitting unsupported str8 headers, and a revision to the end-of-life date for the 2.x line. The 2.x line is still being maintained and is still receiving the same security fixes, which matters if you are pinned there.

One inconsistency to expect: the releases badge in the README still points at the old `neuecc/MessagePack-CSharp` path while the repository now lives at `MessagePack-CSharp/MessagePack-CSharp`. The badge resolves, but the ownership move is recent enough that other references in the wild may be stale too.

How to build it, and who is in the repository

The build is script-driven rather than CLI-driven, with `init.cmd`, `init.ps1` and `installcredprovider.ps1` at the root, which is the pattern for a repository that expects a contributor credential helper to be configured first. `Directory.Build.props`, `Directory.Build.rsp` and `Directory.Build.targets` mean build settings are shared across every project in the solution, and `Directory.Packages.props` means package versions are centrally managed. There is a pinned `global.json` for the SDK version and a `nuget.config`.

The tree also contains `sandbox/` for manual testing, `tools/`, `doc/` with its own migration guide, `tests/`, and `opensource.snk`, which is a strong-name key file checked in so builds sign identically across machines. `graph.xlsx` and `stylecop.json` suggest style and dependency analysis are part of the workflow rather than afterthoughts.

The README closes with a code of conduct and a .NET Foundation notice, which tells you this is a foundation-governed project now, not a solo maintainer's repository. The 148 open issues and 775 forks are consistent with a library at that stage: a large amount of downstream dependency, and a contributor queue.

Editorial conclusion

MessagePack for C# is the rare serialization library where the README's length is justified by the surface area. There are two APIs for every operation, three resolver strategies, a source generator for ahead-of-time compilation, a disallow list for named-type deserialization, and a migration guide for each major version. What ties it together is a single design choice: members carry integer keys rather than string names, so the wire format is fixed at compile time and the serializer can write it without reflection. That is why the performance claims exist at all. The costs are equally clear. Key numbers become a public contract that cannot be reordered, and the project has had to defend itself recently, with an advisory on nested arrays exceeding buffer lengths fixed in both the 3.x and 2.x lines on the same day. Read the security section, decide whether indexed keys or contractless string keys fit your schema, and if you are on Unity or Xamarin, plan around the AOT path from the start.

Frequently asked questions

What is MessagePack used for?

MessagePack is a binary serialisation format, so it is used wherever you want compact, fast payloads instead of text. Typical cases are caches, inter-service messages, game state, and anything where JSON's parsing cost shows up in profiles. The C# implementation targets those cases specifically, and the README calls out games, distributed computing, microservices and data caches as the places where the performance difference is worth having.

How fast is msgpack?

For this C# implementation, the project claims 10x faster than MsgPack-Cli and faster than other C# serialisers, and it ships a benchmark directory plus a comparison section against protobuf, JSON and ZeroFormatter. The speed comes from the design rather than a trick: integer keys mean no property names on the wire, and the source generator means no reflection at runtime. Measure your own types, since large numbers of small objects and repeated strings behave differently from big flat buffers.

Should I use MessagePack or System.Text.Json for a .NET service?

System.Text.Json ships with the framework, produces readable output, and has no schema-numbering decisions attached. MessagePack wins on payload size and throughput, at the cost of opaque payloads and integer keys that become a versioning contract. If your traffic is internal RPC or cache traffic where both ends are under your control, MessagePack is a reasonable pick. If a human ever needs to read or hand-edit the payload, JSON usually wins.

Is typeless deserialization in MessagePack-CSharp safe?

Typeless mode writes type information into the payload, which means the payload decides what type to construct. That is the classic deserialisation gadget risk, and this project mitigates it with a default disallow list of named types that has been extended repeatedly, including in the 3.1.9 release. Treat typeless mode as untrusted-input-only unless the payload channel is fully controlled.

Official sources

  1. Issues
  2. MessagePack-CSharp/MessagePack-CSharp on GitHub
  3. README
  4. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/messagepack-csharp-messagepack-csharp.svg)](https://hysenlabs.com/projects/messagepack-csharp-messagepack-csharp)