Skip to main content

Documentation 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.

This guide walks you through adding Prowl.Echo to a .NET project and performing your first serialize/deserialize round-trip. By the end you will have serialized a typed object to an EchoObject graph, written that graph to both a text string and a binary file, and read it back — all with real, production-ready code.
Echo serializes fields, not properties. Public fields are included automatically. Private fields require the [SerializeField] attribute. Properties — whether auto-implemented or computed — are never serialized by design.
1

Install via NuGet

Add the Prowl.Echo package to your project using the .NET CLI:
dotnet add package Prowl.Echo
Or add it directly to your .csproj file:
<ItemGroup>
  <PackageReference Include="Prowl.Echo" Version="2.2.1" />
</ItemGroup>
The source generator is bundled inside the same NuGet package — no separate analyzer package is required.
2

Serialize and deserialize a basic object

Define a class with public fields and call Serializer.Serialize to produce an EchoObject graph. Pass that graph to Serializer.Deserialize<T> to reconstruct the original value.
using Prowl.Echo;

public class Player
{
    public string Name = "Player";
    public int Health = 100;
    public float Speed = 5.0f;
    public List<string> Inventory = new();
}

// --- Serialize ---
var player = new Player
{
    Name = "Hero",
    Health = 200,
    Speed = 7.5f,
    Inventory = { "Sword", "Shield" }
};

EchoObject echo = Serializer.Serialize(player);

// --- Deserialize ---
Player restored = Serializer.Deserialize<Player>(echo)!;

Console.WriteLine(restored.Name);    // Hero
Console.WriteLine(restored.Health);  // 200
The EchoObject returned by Serialize is a live in-memory graph. You can inspect individual nodes, compare two graphs, or patch values before writing to any output format.
3

Round-trip to text

Call WriteToString() on the EchoObject to produce a human-readable text representation. Read it back with EchoObject.ReadFromString(string), then deserialize as normal.
using Prowl.Echo;

var player = new Player { Name = "Hero", Health = 200 };
EchoObject echo = Serializer.Serialize(player);

// Write to text
string text = echo.WriteToString();
Console.WriteLine(text);

// Read back from text
EchoObject fromText = EchoObject.ReadFromString(text);
Player restored = Serializer.Deserialize<Player>(fromText)!;

Console.WriteLine(restored.Name);   // Hero
Console.WriteLine(restored.Health); // 200
Text format is human-readable and useful for configuration files, save data, and debugging. For files on disk, WriteToString(FileInfo) and ReadFromString(FileInfo) overloads are also available.
4

Round-trip to binary

Binary format produces smaller output and is faster to read and write, making it ideal for I/O-bound workloads such as level data or network packets. Use WriteToBinary and EchoObject.ReadFromBinary.
using Prowl.Echo;

var player = new Player { Name = "Hero", Health = 200 };
EchoObject echo = Serializer.Serialize(player);

var file = new FileInfo("player.echo");

// Write to binary file
echo.WriteToBinary(file);

// Read back from binary file
EchoObject fromBinary = EchoObject.ReadFromBinary(file);
Player restored = Serializer.Deserialize<Player>(fromBinary)!;

Console.WriteLine(restored.Name);   // Hero
Console.WriteLine(restored.Health); // 200
You can also serialize to and from a BinaryWriter / BinaryReader directly when you need more control over the stream:
using var stream = new MemoryStream();
using var writer = new BinaryWriter(stream);
echo.WriteToBinary(writer);

stream.Position = 0;
using var reader = new BinaryReader(stream);
EchoObject fromStream = EchoObject.ReadFromBinary(reader);
5

Use [GenerateSerializer] for maximum performance

Annotate your class or struct with [GenerateSerializer] and mark it partial. The source generator will emit an optimized ISerializable implementation at compile time that inlines primitive construction and bypasses the reflection pipeline entirely.
using Prowl.Echo;

[GenerateSerializer]
public partial class Player
{
    public string Name = "Player";
    public int Health = 100;
    public float Speed = 5.0f;
    public List<string> Inventory = new();
}

// Usage is identical — no API changes required
var player = new Player { Name = "Hero", Health = 200 };
EchoObject echo = Serializer.Serialize(player);
Player restored = Serializer.Deserialize<Player>(echo)!;
For small, stable value types such as vectors or colors, combine [GenerateSerializer] with [FixedEchoStructure] to enable compact positional serialization — skipping field names entirely for the smallest possible output and the highest possible throughput:
[GenerateSerializer]
[FixedEchoStructure]
public partial struct Vector3
{
    public float X;
    public float Y;
    public float Z;
}
Use [GenerateSerializer] on all types in your production code. It consistently outperforms reflection-based serialization and has no runtime overhead. The partial keyword is the only requirement — your public API and field layout do not need to change.

Build docs developers (and LLMs) love