Serialization¶
This page covers the public serialization surface in Nalix.Codec.Serialization.
Source mapping¶
src/Nalix.Codec/Serialization/IFormatter.cssrc/Nalix.Codec/Serialization/FormatterProvider.cssrc/Nalix.Codec/Serialization/LiteSerializer.cssrc/Nalix.Abstractions/Primitivessrc/Nalix.Codec/Serialization/Formatters/Collectionssrc/Nalix.Codec/Serialization/Formatters/Automatic
Main types¶
IFormatter<T>FormatterProviderLiteSerializer
What it does¶
This layer provides:
- primitive, collection, and memory formatters
- automatic object and struct formatters
- a provider that resolves the right formatter
- a lightweight serializer entry point
Supported type groups¶
The current source supports these groups directly:
- unmanaged primitives and value types
stringandstring[]- nullable value types such as
int?,Guid?,DateTime? - unmanaged arrays such as
int[],Guid[],DateTime[] - nullable arrays such as
int?[],Guid?[] - enum values, enum arrays, and enum lists
List<T>Dictionary<TKey, TValue>Queue<T>Stack<T>HashSet<T>Memory<T>andReadOnlyMemory<T>for unmanaged element typesValueTuplearity 2 through 5- automatic class and struct serialization through generated formatters
Built-in primitive coverage¶
The provider registers built-in formatters for:
char,byte,sbyteshort,int,longushort,uint,ulongfloat,double,decimalboolGuidDateOnly,DateTime,TimeOnly,TimeSpan,DateTimeOffset
Collection behavior¶
Collection support is broader than the old docs implied:
- arrays support unmanaged, enum, nullable-value, and reference-type elements
List<T>supports value, nullable-value, enum, and reference-type elementsDictionary<TKey, TValue>is supported through a dedicated formatterQueue<T>,Stack<T>, andHashSet<T>currently reject most class element types exceptstring
Automatic object and struct serialization¶
When no explicit formatter is registered, the provider can create formatters for:
- classes through
ObjectFormatter<T>orNullableObjectFormatter<T> - structs through
StructFormatter<T>
Types marked with SerializePackableAttribute are treated as explicitly packable and skip the nullable-object wrapper path.
Member discovery rules¶
Automatic object and struct serialization is field-based.
- instance fields are the real serialization targets
- public and non-public instance fields are discovered
- static fields are ignored
- const fields are not part of instance state and are not serialized
- properties are not serialized directly
- auto-properties work because the serializer can target their compiler-generated backing fields
- custom or computed properties without a compiler-generated backing field are not serialized as standalone members
In practice, the safest member shapes are:
- instance fields
- auto-properties with a backing field such as
get; set;,get; private set;, or similar compiler-backed forms
The less predictable shapes are:
- getter-only or
init-only auto-properties, because their backing fields are typically readonly - custom properties that forward to other fields or compute values on demand
If a model depends on constructor-only initialization or immutable invariants, prefer a custom IFormatter<T> instead of relying on automatic member discovery.
Attributes and properties¶
The serialization attributes can be applied to either fields or properties:
SerializeOrderSerializeHeaderSerializeIgnore
For automatic serialization, those attributes are most reliable on:
- fields
- auto-properties with compiler-generated backing fields
Applying serialization attributes to a property does not make the serializer call the property's getter or setter directly. The property metadata is used to influence field discovery, usually by mapping the property attribute to its backing field when one exists.
Important limits¶
Some collection shapes are intentionally restricted
Memory<T> and ReadOnlyMemory<T> only support unmanaged element types.
Queue<T>, Stack<T>, and HashSet<T> do not support arbitrary class elements today.
ValueTuple support currently stops at arity 5.
Readonly and immutable members
Automatic deserialization currently writes field values during object reconstruction. Mutable fields and standard auto-properties are the safest shapes. If a type relies on strict readonly or constructor-only semantics, document that intent with a custom formatter instead of depending on generated field assignment.
Basic usage¶
byte[] bytes = LiteSerializer.Serialize(model);
MyModel clone = LiteSerializer.Deserialize<MyModel>(bytes);
Resolution flow¶
flowchart LR
A["LiteSerializer"] --> B["FormatterProvider.Get()"]
B --> C["Built-in formatter"]
B --> D["Auto collection formatter"]
B --> E["Auto object / struct formatter"]
C --> F["Serialize / Deserialize"]
D --> F
E --> F
FormatterProvider¶
FormatterProvider is the registry/resolution layer for formatters.
Use it when you need lower-level control than LiteSerializer.
Example¶
If you need to override the default behavior for one type, register your formatter first: