Skip to main content
Prowl.Echo has dedicated format handlers for every major .NET collection type. You call Serializer.Serialize and Serializer.Deserialize the same way you would for a plain object — the library automatically routes each collection to the correct handler based on the runtime type.

Supported Collection Types

List<T>

Serialized as EchoType.List — an ordered sequence of EchoObject entries.

T[] / T[,] / T[][]

Single-dimensional, multi-dimensional, and jagged arrays. Stored as a compound with an "array" or "dimensions" + "elements" layout.

Dictionary<TKey, TValue>

String keys → flat compound. Non-string keys → compound with an "entries" list of {key, value} pairs.

HashSet, Stack, Queue, LinkedList

Each has a dedicated format handler (HashSetFormat, StackFormat, QueueFormat, LinkedListFormat). Other generic ICollection&lt;T&gt; types (such as ObservableCollection&lt;T&gt; and SortedSet&lt;T&gt;) are handled by CollectionFormat.

Lists

List<T> is serialized as an EchoObject with TagType == EchoType.List. Each element is serialized using the declared element type, so polymorphic elements automatically receive a $type tag when needed.

Nested Lists

Lists of lists work without any special configuration:

Arrays

Single-Dimensional Arrays

Stored as a compound containing a "array" key whose value is an EchoType.List of the elements:

Jagged Arrays

Jagged arrays (T[][]) are supported. Each inner array is itself serialized as a compound with an "array" key:

Multi-Dimensional Arrays

Multi-dimensional arrays (T[,], T[,,], etc.) are stored as a compound with a "dimensions" entry (a serialized int[] of lengths) and an "elements" list of all values in row-major order:

Dictionaries

String Keys — Flat Compound Layout

When the key type is string, Echo serializes the dictionary as a flat EchoType.Compound where each entry becomes a named tag. This produces the most compact and human-readable output:
The resulting EchoObject looks like:

Non-String Keys — Entry-List Layout

When keys are not strings (e.g., int, Guid, enum, or a custom class), Echo wraps all entries in an "entries" list, where each element is a compound with "key" and "value" sub-tags:

Nested Dictionaries

Arbitrary nesting is fully supported:

HashSet

HashSet<T> is handled by HashSetFormat, which serializes the set as an EchoType.List. Other set-like types such as SortedSet<T> implement ICollection<T> and are routed through CollectionFormat instead:
Null elements in HashSet<string?> are preserved:

Queue

Queue<T> is serialized as an EchoType.List, preserving FIFO order. Elements dequeue in the same order after deserialization:

Stack

Stack<T> is serialized as an EchoType.List. Pop order is preserved across the round-trip:

LinkedList

LinkedList<T> is serialized as an EchoType.List in node order from First to Last:

Arbitrary ICollection<T> Implementations

Any generic type that implements ICollection<T> (and is not a Dictionary) is handled by CollectionFormat. This includes ObservableCollection<T>, Collection<T>, and similar BCL types:

Deeply Nested and Mixed Collections

Echo handles arbitrary nesting of heterogeneous collection types:

Type Preservation in Collection Elements

When a collection’s element type is abstract, an interface, or object, Echo automatically embeds $type tags for concrete elements so they survive deserialization:
Empty collections round-trip correctly. An empty List<int>, Queue<string>, Stack<double>, or LinkedList<int> serializes and deserializes back to an empty instance of the same type.