Use this file to discover all available pages before exploring further.
Prowl.Echo serializes .NET objects into an intermediate EchoObject representation that can then be written to binary or text. The Serializer class is the single entry point for all serialization and deserialization work, and it requires no configuration to get started — just call Serialize and Deserialize.
Echo serializes fields, not properties. Public fields are serialized automatically. Private fields require [SerializeField].
using Prowl.Echo;public class Player{ public string Name = "Hero"; public int Health = 100; public float Speed = 5.0f; [SerializeField] private int _secretScore = 0; // Properties are NOT serialized — by design. public bool IsAlive => Health > 0;}
2
Serialize the object
var player = new Player { Name = "Arden", Health = 200 };EchoObject echo = Serializer.Serialize(player);
Echo’s reflection-based serializer walks the full inheritance hierarchy and collects fields that pass the following rules:
Condition
Serialized?
public field
✅ Yes
private / protected field with [SerializeField]
✅ Yes
private / protected field without [SerializeField]
❌ No
Any field with [SerializeIgnore] or [NonSerialized]
❌ No
Properties
❌ No (by design)
public class Example{ public string VisibleField = "serialized"; [SerializeField] private int _hiddenField = 42; // included because of the attribute [SerializeIgnore] public string SkippedField = "gone"; // excluded even though it's public public string IgnoredProperty => "never serialized";}
Prowl.Echo mimics Unity’s serializer in this regard: only fields matter, and the attribute controls opt-in for non-public members.
Standard reference-type classes work out of the box. All public fields, and any private fields marked [SerializeField], are serialized.
public class Weapon{ public string Name = "Sword"; public int Damage = 25; public float Range = 1.5f;}EchoObject echo = Serializer.Serialize(new Weapon { Name = "Axe", Damage = 40 });Weapon? weapon = Serializer.Deserialize<Weapon>(echo);
Value types are handled identically. Echo’s fast path bypasses the full reflection pipeline for primitive structs.
public struct Point{ public float X; public float Y;}EchoObject echo = Serializer.Serialize(new Point { X = 3.0f, Y = 7.5f });Point pt = Serializer.Deserialize<Point>(echo);
For structs with a stable, never-changing field order (like Vector3), add [FixedEchoStructure] to enable compact positional serialization — great for network packets and binary-heavy workloads.
Records expose their data through properties by default. To make record data serializable, declare backing fields explicitly, or use a record struct with public fields.
// Use public fields for Echo compatibilitypublic record class Config{ public string Host = "localhost"; public int Port = 8080;}EchoObject echo = Serializer.Serialize(new Config { Host = "example.com", Port = 443 });Config? cfg = Serializer.Deserialize<Config>(echo);
For int, float, double, bool, string, long, byte, char, uint, short, ulong, ushort, sbyte, decimal, and byte[], Echo skips the full format-selection pipeline entirely and constructs the EchoObject in a single switch expression. This makes primitive round-trips as fast as possible.
// All of these hit the fast path:EchoObject i = Serializer.Serialize(42);EchoObject f = Serializer.Serialize(3.14f);EchoObject b = Serializer.Serialize(true);EchoObject s = Serializer.Serialize("hello");
DeserializeInto writes serialized data directly into an object you already have, without allocating a new one. Non-serialized fields (GPU handles, history buffers, event subscribers, etc.) are untouched.
var liveEnemy = new Enemy(); // already tracked by the game engineEchoObject savedData = LoadSavedData();Serializer.DeserializeInto(savedData, liveEnemy);// liveEnemy now has restored field values, but its// internal event subscriptions and caches are preserved.
DeserializeInto only works with compound (object) types. It will silently no-op on primitives, arrays, and null inputs.
When you know the target type at compile time, pass it as the first argument to let Echo skip a runtime type check and produce cleaner serialized output (no unnecessary $type tag):
EchoObject echo = Serializer.Serialize(typeof(Player), player, new SerializationContext());
This is useful when storing objects in typed containers or when building custom serialization pipelines.
Echo caches serializable field lists and type name lookups in ConcurrentDictionary instances for performance. If you dynamically load or unload assemblies at runtime (plug-in systems, hot-reload workflows), call ClearCache() afterwards:
// After hot-reloading an assembly:Serializer.ClearCache();
Calling ClearCache() also clears TypeNameRegistry’s lookup tables, so the next serialization call for each type will re-compute everything from reflection. Avoid calling it in hot paths.