The Prowl.Echo source generator analyzes your types at compile time and emits optimizedDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/ProwlEngine/Prowl.Echo/llms.txt
Use this file to discover all available pages before exploring further.
Serialize and Deserialize methods that implement ISerializable. By inlining primitive construction and bypassing the reflection pipeline entirely, source-generated types are significantly faster than reflection-based serialization — outperforming both System.Text.Json and Newtonsoft.Json across all benchmarks.
Getting Started
Add[GenerateSerializer] to any partial class or struct. The partial keyword is required so the generator can add the generated code alongside your definition.
Serialize and Deserialize method in a separate partial file. You use Serializer.Serialize and Serializer.Deserialize exactly as normal — the generated code is called automatically:
The source generator is bundled in the
Prowl.Echo NuGet package under analyzers/dotnet/cs. No separate package or manual reference is needed.Requirements
partial keyword
The class or struct must be declared
partial — the generator adds a second partial declaration with the generated code.Fields only
Only public fields (and private fields with
[SerializeField]) are generated. Properties are never serialized.What the Generator Inlines
The generator classifies each field into aFieldTypeCategory and emits the most efficient code for its type. Inline handling is used for the following categories — all other types, including Nullable<T>, fall back to Serializer.Serialize/Serializer.Deserialize:
| Field Type | Serialize Expression | Deserialize Expression |
|---|---|---|
byte, sbyte, short, ushort, int, uint, long, ulong, float, double, decimal, bool | new EchoObject(field) | echo.ByteValue / .IntValue / .FloatValue etc. |
char | new EchoObject((byte)field) | (char)echo.ByteValue |
string | new EchoObject(field) (null-safe) | echo.StringValue |
byte[] | new EchoObject(field) (null-safe) | echo.ByteArrayValue |
enum | new EchoObject(EchoType.Int, (int)field) | (MyEnum)echo.IntValue |
Guid | new EchoObject(EchoType.String, field.ToString()) | Guid.Parse(echo.StringValue) |
DateTime | compound with "date" key (binary-encoded long via ToBinary()) | DateTime.FromBinary(echo.Get("date").LongValue) |
TimeSpan | compound with "ticks" key (long ticks) | new TimeSpan(echo.Get("ticks").LongValue) |
List<T> of known type | inline list iteration | inline list construction |
T[] of known type | compound with "array" key | inline array construction |
Dictionary<string, T> of known type | inline compound | inline dictionary construction |
All other types (including Nullable<T>) | Serializer.Serialize(typeof(T), field, ctx) | Serializer.Deserialize(echo, typeof(T), ctx) |
List<T>, T[], Dictionary<string, T>), the element type must itself be a simple known category (primitive, string, enum, or Guid). Nested collections or complex element types fall back to Serializer.Serialize.
Using Attributes with Generated Code
All serialization attributes work with[GenerateSerializer]:
Serialize method wraps conditional fields in the appropriate if guards. [FormerlySerializedAs] generates additional else if (value.TryGet("hp", out var ...)) branches in the Deserialize method.
Performance Comparison
With[GenerateSerializer] and [FixedEchoStructure] on a complex object graph (20 nested objects, 100-element arrays, 50-entry dictionaries, collections):
vs. Manual ISerializable
[GenerateSerializer] auto-generates ISerializable for you. You only need to implement it manually when you need logic the generator cannot express — for example, computing a derived field or migrating data between schema versions. See Custom Serialization for the manual approach.