LightProto 1.0.0
See the version list below for details.
dotnet add package LightProto --version 1.0.0
NuGet\Install-Package LightProto -Version 1.0.0
<PackageReference Include="LightProto" Version="1.0.0" />
<PackageVersion Include="LightProto" Version="1.0.0" />
<PackageReference Include="LightProto" />
paket add LightProto --version 1.0.0
#r "nuget: LightProto, 1.0.0"
#:package LightProto@1.0.0
#addin nuget:?package=LightProto&version=1.0.0
#tool nuget:?package=LightProto&version=1.0.0
LightProto 🚀
A high‑performance, Native AOT–friendly, production ready Protocol Buffers implementation for C#/.NET, powered by source generators.
Why LightProto? 🤔
protobuf-net is a popular Protocol Buffers implementation in .NET, but some scenarios (especially Native AOT) can be challenging due to runtime reflection and dynamic generation. LightProto addresses this with compile-time code generation and a protobuf-net–style API.
Features ✨
- Source generator–powered serializers/parsers at compile time
- AOT-friendly by design, No IL warnings
- minimal C# 9.0 requirement for broader compatibility (including Unity)
- No third-party dependencies
- protobuf-net–style Serializer API and familiar attributes
- Performance is about 20%-50% better than protobuf-net, see benchmarks below
- Target frameworks: netstandard2.0, net8.0, net9.0, net10.0
- Stream and IBufferWriter<byte> serialization or ToByteArray
- ReadOnlySpan<byte>/ReadOnlySequence<byte>/Stream deserialization
Rich built-in type support
- .NET primitives (
byte,int,bool,char,double, etc.) string,decimal,Half,Int128,UInt128,Guid,Rune,BigIntegerTimeSpan,DateTime,DateTimeOffset,TimeOnly,DateOnly,TimeZoneInfoComplex,Plane,Quaternion,Matrix3x2,Matrix4x4,Vector2,Vector3,Vector4Uri,Version,StringBuilder,BitArray,CultureInfoNullable<>,Lazy<>T[],List<>,LinkedList<>,Queue<>,Stack<>,HashSet<>,SortedSet<>Dictionary<,>,SortedList<,>,SortedDictionary<,>,ReadOnlyDictionary<,>Collection<>,ReadOnlyCollection<>,ObservableCollection<>,ReadOnlyObservableCollection<>IEnumerable<>,ICollection<>,IList<>,IReadOnlyCollection<>,IReadOnlyList<>,ISet<>IDictionary<,>,IReadOnlyDictionary<,>ConcurrentBag<>,ConcurrentQueue<>,ConcurrentStack<>,ConcurrentDictionary<,>,BlockingCollection<>ImmutableList<>,ImmutableArray<>,ImmutableHashSet<>,ImmutableDictionary<,>
Quick Start ⚡
Install from NuGet:
dotnet add package LightProto
Define your contracts (partial classes) using LightProto attributes:
using LightProto;
[ProtoContract]
public partial class Person
{
[ProtoMember(1)]
public string Name { get; set; } = string.Empty;
[ProtoMember(2)]
public int Age { get; set; }
}
var person = new Person { Name = "Alice", Age = 30 };
// Serialize to a byte[]
byte[] bytes = person.ToByteArray();
// person.ToByteArray(Person.ProtoWriter); // use this overload when .netstandard2.0
// Or serialize to a Stream
using var stream = new MemoryStream();
Serializer.Serialize(stream, person);
// Serializer.Serialize(stream, person, Person.ProtoWriter); // use this overload when .netstandard2.0
byte[] data = stream.ToArray();
// Deserialize from byte[] (ReadOnlySpan<byte> overload will be used)
Person fromBytes = Serializer.Deserialize<Person>(bytes);
// Person fromBytes = Serializer.Deserialize<Person>(bytes,Person.ProtoReader); // use this overload when .netstandard2.0
// Or deserialize from Stream
using var input = new MemoryStream(data);
Person fromStream = Serializer.Deserialize<Person>(input);
// Person fromStream = Serializer.Deserialize<Person>(input,Person.ProtoReader); // use this overload when .netstandard2.0
Migration from protobuf-net 🔁
Most code migrates by swapping the namespace and marking your types partial.
- Replace ProtoBuf with LightProto.
- Mark serializable types as partial.
- Remove runtime configuration (e.g., RuntimeTypeModel). LightProto generates code at compile time.
Example:
- using ProtoBuf;
+ using LightProto;
[ProtoContract]
- public class Person
+ public partial class Person
{
[ProtoMember(1)]
public string Name { get; set; } = string.Empty;
[ProtoMember(2)]
public int Age { get; set; }
}
var myObject = new Person { Name = "Alice", Age = 30 };
// Serialization
var stream = new MemoryStream();
Serializer.Serialize(stream, myObject);
byte[] data = stream.ToArray();
// Deserialization
var obj = Serializer.Deserialize<Person>(new ReadOnlySpan<byte>(data));
Common replacements:
- RuntimeTypeModel and runtime surrogates → use compile-time attributes (see Surrogates below).
- Non-partial types → mark as partial to enable generator output.
Need to know 🧠
LightProto aims to minimize differences from protobuf-net; notable ones include:
Partial classes required
protobuf-net: partial not required
LightProto: mark [ProtoContract] types as partial so the generator can emit code.
Serialization APIs
protobuf-net: Serializer.Serialize<T>(...) and Serializer.Deserialize<T>(...)
LightProto:
- Generic-constrained APIs:
Serializer.Serialize<T>(...)andSerializer.Deserialize<T>(...)require thatTmust implementIProtoParser<T>(i.e., a generated message type); - Explicitly passing
IProtoReader/WriterAPIs:Serializer.Serialize<T>(..., IProtoWriter<T>)andSerializer.Deserialize<T>(..., IProtoReader<T>)for types withoutIProtoParser<T>implementation (e.g., primitive types). - Non-Generic-constrained APIs:
Serializer.SerializeDynamically<T>(...)andSerializer.DeserializeDynamically<T>(...)resolveIProtoReader/Writerat runtime (not AOT-safe for generic container shapes). They only work in AOT whenTis a[ProtoContract]marked type or a primitive/known built-in type; using generic container shapes likeNullable<>,Lazy<>,List<>,Dictionary<,>etc. will fail at runtime on AOT due to missing type metadata.
int a=10;
ArrayBufferWriter<byte> writer = new ArrayBufferWriter<byte>();
LightProto.Serializer.Serialize<int>(writer, a,Int32ProtoParser.Writer); // must pass writer
var bytes = a.ToByteArray(Int32ProtoParser.Writer); // extension method
int result = LightProto.Serializer.Deserialize<int>(bytes,Int32ProtoParser.Reader); // must pass reader
List<int> list=[1,2,3];
var bytes = list.ToByteArray(Int32ProtoParser.Writer.GetCollectionWriter());// extension method
ArrayBufferWriter<byte> writer = new ArrayBufferWriter<byte>();
LightProto.Serializer.Serialize(writer, list,Int32ProtoParser.Writer.GetCollectionWriter()); // pass collection writer
LightProto.Serializer.SerializeDynamically<List<int>>(writer, list); // dynamic API, not AOT-safe approach.
List<int> arr2=LightProto.Serializer.Deserialize(bytes,Int32ProtoParser.Reader.GetListReader()); // pass element reader
List<int> arr3=LightProto.Serializer.DeserializeDynamically<List<int>>(bytes); // dynamic API, not AOT-safe approach.
.netstandard
In .netstandard targeting platform such as .NET Framework, we can't use static virtual members in interface to find ProtoReader/Writer.
So LightProto requires user to specify a ProtoWriter when serializing and a ProtoReader when deserializing.
For [ProtoContract] marked MessageType the ProtoReader/Writer is generated by LightProto, just use MessageType.ProtoReader/Writer.
For primitive types, LightProto has predefined in LightProto.Parser namespace, such as LightProto.Parser.DateTimeParser.
If you don't need AOT support, you can use dynamic APIs Serializer.SerializeDynamically<T> and Serializer.DeserializeDynamically<T> without passing ProtoReader/Writer.
IExtensible
protobuf-net: supports IExtensible for dynamic extensions
LightProto: IExtensible is defined for compatibility only and has no effect
Surrogates
protobuf-net: can register surrogates via RuntimeTypeModel at runtime
LightProto: You can specify a custom ProtoParserType for MessageType.
For example, MessageType is Person, custom ProtoParserType is PersonProtoParser, you can use following attribute to specify a custom ProtoParserType, list as precedence order:
- member level:
[ProtoMember(1,ParserType=typeof(PersonProtoParser))] - class level:
[ProtoParserTypeMap(typeof(Person), typeof(PersonProtoParser))] - module/assembly level:
[ProtoParserTypeMap(typeof(Person), typeof(PersonProtoParser))](messageType and parserType should not be in same assembly, If so, type level attribute is suggested.) - type level:
[ProtoParserTypeMap(typeof(Person), typeof(PersonProtoParser))] - default:
LightProto.Parser.PersonProtoParser
The ProtoParserType must implement IProtoParser<MessageType>. The easiest way is defining a SurrogateType with [ProtoContract] and mark [ProtoSurrogateFor<MessageType>].
Example for Person(can be any type):
[ProtoParserType(typeof(PersonProtoParser))] // type level ProtoParser
public class Person
{
public string Name {get; set;}
Person(){}
public static Person FromName(string name) => new Person() { Name = name };
}
[ProtoContract]
[ProtoSurrogateFor<Person>] // mark this to tell source generator generate IProtoParser<Person> instead of `IProtoParser<PersonProtoParser>`
public partial struct PersonProtoParser
{
[ProtoMember(1)]
internal string Name { get; set; }
public static implicit operator Person(PersonProtoParser parser) //must define implicit conversions for surrogate type
{
return Person.FromName(parser.Name);
}
public static implicit operator PersonProtoParser(Person value) //must define implicit conversions for surrogate type
{
return new PersonProtoParser() { Name = value.Name };
}
}
[assembly: ProtoParserTypeMap(typeof(Person), typeof(PersonProtoParser))] // assembly level ProtoParser
[ProtoParserTypeMap(typeof(Person), typeof(PersonProtoParser))] // class level ProtoParser
public class MessageContract
{
[ProtoMember(1,ParserType=typeof(PersonProtoParser))] //member level ProtoParser
public Person Person {get; set;}
}
You can also read/write raw binary data, for example see:DateOnlyProtoParser
StringIntern
protobuf-net: Use RuntimeTypeModel.Default.StringInterning = true; to enable string interning globally
LightProto: [StringIntern] attribute can apply to individual string members/class/module/assembly.
RuntimeTypeModel
Not supported; all configuration is static via attributes and generated code
If you encounter different behavior versus protobuf-net, please open an issue.
Unity Support 🎮
LightProto supports C# 9, making it compatible with Unity projects targeting .NET Standard 2.0. You can use LightProto in Unity by following the same installation and usage instructions as for other .NET projects.
IL2CPP builds are supported since LightProto is AOT-friendly.
Performance & Benchmarks 📊
The following benchmarks compare serialization performance between LightProto, protobuf-net, and Google.Protobuf.
You can reproduce these by cloning the repo and running tests/Benchmark.
BenchmarkDotNet v0.15.3, Windows 11 (10.0.26100.4652/24H2/2024Update/HudsonValley)
AMD Ryzen 7 5800X 3.80GHz, 1 CPU, 16 logical and 8 physical cores
.NET SDK 10.0.100-rc.1.25451.107
[Host] : .NET 8.0.20 (8.0.20, 8.0.2025.41914), X64 RyuJIT x86-64-v3
.NET 10.0 : .NET 10.0.0 (10.0.0-rc.1.25451.107, 10.0.25.45207), X64 RyuJIT x86-64-v3
.NET 8.0 : .NET 8.0.20 (8.0.20, 8.0.2025.41914), X64 RyuJIT x86-64-v3
.NET 9.0 : .NET 9.0.9 (9.0.9, 9.0.925.41916), X64 RyuJIT x86-64-v3
| Method | Job | Runtime | Mean | Error | StdDev | Ratio | RatioSD | Allocated | Alloc Ratio |
|---|---|---|---|---|---|---|---|---|---|
| Serialize_ProtoBuf_net | .NET 10.0 | .NET 10.0 | 645.6 μs | 12.70 μs | 11.88 μs | 1.39 | 0.03 | 526.41 KB | 1.03 |
| Serialize_GoogleProtoBuf | .NET 10.0 | .NET 10.0 | 539.9 μs | 10.71 μs | 12.75 μs | 1.16 | 0.03 | 512.95 KB | 1.00 |
| Serialize_LightProto | .NET 10.0 | .NET 10.0 | 465.1 μs | 7.88 μs | 6.99 μs | 1.00 | 0.02 | 512.95 KB | 1.00 |
| Serialize_ProtoBuf_net | .NET 8.0 | .NET 8.0 | 757.0 μs | 12.80 μs | 11.98 μs | 1.42 | 0.04 | 526.41 KB | 1.03 |
| Serialize_GoogleProtoBuf | .NET 8.0 | .NET 8.0 | 553.9 μs | 10.97 μs | 9.72 μs | 1.04 | 0.03 | 512.95 KB | 1.00 |
| Serialize_LightProto | .NET 8.0 | .NET 8.0 | 531.9 μs | 10.52 μs | 14.04 μs | 1.00 | 0.04 | 512.95 KB | 1.00 |
| Serialize_ProtoBuf_net | .NET 9.0 | .NET 9.0 | 712.6 μs | 13.61 μs | 12.73 μs | 1.39 | 0.04 | 526.41 KB | 1.03 |
| Serialize_GoogleProtoBuf | .NET 9.0 | .NET 9.0 | 546.7 μs | 10.70 μs | 16.33 μs | 1.07 | 0.04 | 512.95 KB | 1.00 |
| Serialize_LightProto | .NET 9.0 | .NET 9.0 | 513.6 μs | 10.15 μs | 13.89 μs | 1.00 | 0.04 | 512.95 KB | 1.00 |
| Method | Job | Runtime | Mean | Error | StdDev | Ratio | RatioSD | Allocated | Alloc Ratio |
|---|---|---|---|---|---|---|---|---|---|
| Deserialize_ProtoBuf_net | .NET 10.0 | .NET 10.0 | 569.2 μs | 10.88 μs | 12.53 μs | 1.38 | 0.04 | 562 KB | 0.84 |
| Deserialize_GoogleProtoBuf | .NET 10.0 | .NET 10.0 | 441.4 μs | 8.67 μs | 10.64 μs | 1.07 | 0.04 | 648.7 KB | 0.97 |
| Deserialize_LightProto | .NET 10.0 | .NET 10.0 | 411.5 μs | 8.08 μs | 9.92 μs | 1.00 | 0.03 | 665.95 KB | 1.00 |
| Deserialize_ProtoBuf_net | .NET 8.0 | .NET 8.0 | 688.0 μs | 13.51 μs | 15.56 μs | 1.55 | 0.05 | 562 KB | 0.84 |
| Deserialize_GoogleProtoBuf | .NET 8.0 | .NET 8.0 | 595.5 μs | 11.51 μs | 16.14 μs | 1.34 | 0.04 | 648.7 KB | 0.97 |
| Deserialize_LightProto | .NET 8.0 | .NET 8.0 | 444.8 μs | 8.88 μs | 9.12 μs | 1.00 | 0.03 | 665.95 KB | 1.00 |
| Deserialize_ProtoBuf_net | .NET 9.0 | .NET 9.0 | 662.3 μs | 12.60 μs | 11.17 μs | 1.53 | 0.04 | 562 KB | 0.84 |
| Deserialize_GoogleProtoBuf | .NET 9.0 | .NET 9.0 | 491.7 μs | 9.64 μs | 13.52 μs | 1.14 | 0.04 | 648.7 KB | 0.97 |
| Deserialize_LightProto | .NET 9.0 | .NET 9.0 | 431.9 μs | 8.33 μs | 9.25 μs | 1.00 | 0.03 | 665.95 KB | 1.00 |
Note: Results vary by hardware, runtime, and data model. Please run the benchmarks on your environment for the most relevant numbers.
Working with .proto files 📄
LightProto doesn’t ship a .proto → C# generator for now. You can generate C# using protobuf-net (or other tools), then adapt the output to LightProto (typically replacing the ProtoBuf namespace with LightProto and marking types partial). If something doesn’t work, please file an issue.
If you need a dedicated .proto → C# generator, please vote this issue.
Contributing 🤝
Contributions are welcome! Please see CONTRIBUTING for detailed contribution guidelines.
ARCHITECTURE.md describes the internal design and structure of LightProto, which may be helpful for contributors.
License 📄
MIT License — see LICENSE for details.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- System.Collections.Immutable (>= 9.0.0)
- System.Memory (>= 4.6.3)
- System.Runtime.CompilerServices.Unsafe (>= 6.1.2)
-
net10.0
- No dependencies.
-
net8.0
- No dependencies.
-
net9.0
- No dependencies.
NuGet packages (1)
Showing the top 1 NuGet packages that depend on LightProto:
| Package | Downloads |
|---|---|
|
JK.Mexc.Net
JK.Mexc.Net is a high performance client library for accessing the Mexc REST and Websocket API. All data is mapped to readable models and enum values. Additional features include automatic websocket (re)connection management, an implementation for maintaining a client side order book, easy integration with other exchange client libraries and more. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.2.1 | 117 | 1/26/2026 |
| 1.2.0 | 100 | 1/21/2026 |
| 1.1.1 | 96 | 1/12/2026 |
| 1.1.0 | 96 | 1/10/2026 |
| 1.0.1 | 101 | 1/6/2026 |
| 1.0.0 | 97 | 1/4/2026 |
| 0.9.5 | 178 | 12/24/2025 |
| 0.9.4 | 272 | 12/18/2025 |
| 0.9.3 | 271 | 12/18/2025 |
| 0.9.2 | 277 | 12/17/2025 |
| 0.9.1 | 273 | 12/16/2025 |
| 0.9.0 | 5,272 | 12/12/2025 |
| 0.8.5 | 387 | 11/17/2025 |
| 0.8.4 | 228 | 10/27/2025 |
| 0.8.3 | 113 | 10/25/2025 |
| 0.8.2 | 126 | 10/25/2025 |
| 0.8.1 | 192 | 10/23/2025 |
| 0.8.0 | 196 | 10/20/2025 |
| 0.7.1 | 208 | 10/15/2025 |
| 0.7.0 | 119 | 10/11/2025 |