Class ReflectionSerializer
A general-purpose reflection-based serializer for custom classes and structs. Unlike the
dedicated primitive and Unity-type serializers, ReflectionSerializer handles
arbitrary user-defined types by inspecting their TypeMetadata at runtime -
iterating over discoverable public properties and fields, writing each as a named JSON
property, and reconstructing the object from those properties on read.
Implements
Inherited Members
Namespace: Scylla.Core.Util.Serialization
Assembly: ScyllaCore.dll
Syntax
public sealed class ReflectionSerializer : ITypeSerializer
Remarks
Registration. This serializer is the fallback used by SerializationEngine when no dedicated ITypeSerializer is registered for a given type. It is also created explicitly by the engine for each concrete type that has been decorated with ScyllaSerializableAttribute or that exposes serializable public members.
Property discovery. The set of serialized members is determined by TypeMetadata (and ultimately by TypeMetadataCache). Public properties and fields are included by default; private members require ScyllaIncludeAttribute. Members tagged with ScyllaIgnoreAttribute are always excluded.
Cyclic reference handling. Reference-type values are tracked through ReferenceTracker. Depending on ReferenceHandling:
- Error - throws SerializationException when a cycle is detected.
-
Ignore - writes
nullin place of the back-reference. -
Preserve - emits a
{"$ref": id}marker that is resolved back to the original object during deserialization. The first occurrence of a tracked object also carries an explicit$idmarker when EmitReferenceIDs istrue(the default), so a later$refresolves correctly even if the reader visits properties in a different order than the writer did. A document with no$idmarkers still deserializes correctly via implicit traversal-order tracking.
Type polymorphism. When the runtime type of a value differs from the
declared type (a derived class in a base-typed field, or any value in an
object or interface-typed field), the value is written with a $type
marker and the properties of its runtime type, regardless of
IncludeTypeInfo. The marker is the explicit
TypeAlias of the runtime type when it has
one, otherwise its assembly-qualified name. When
IncludeTypeInfo is enabled, objects whose runtime
type equals the declared type carry the marker as well.
A runtime type that is not serialized by a ReflectionSerializer (a boxed
primitive, an enum, a collection, or a type with a custom serializer) is written as a
{"$type": ..., "$value": ...} envelope. On read, the marker is resolved through
ResolveAlias(string) and must be assignable to the declared
type, otherwise a SerializationException is thrown. A marker that cannot
be resolved falls back to the declared type when that type is concrete, and fails with
a SerializationException when it is abstract or an interface.
Opt-in rule for object and interface declarations. When the
declared type is object or an interface, a marker may only name a type that is
annotated with ScyllaSerializableAttribute (any version or alias),
registered through RegisterAlias(string, Type), or handled by a
dedicated serializer (boxed primitives, enums, supported collections of such types,
and types with a built-in or custom serializer). Any other type fails with a
SerializationException that names the marker and the declared type,
so a hand-edited document cannot make the engine instantiate an arbitrary loaded
type. Concrete and abstract-class declared types only require assignability.
Schema versioning. When IncludeVersionInfo
is enabled and Version is greater than zero, a $version
property is written. On read, if the stored version is older than the current type
version, the migration path resolved by TryBuildMigrationPath(int, int, out IReadOnlyList<MethodInfo>)
is invoked to migrate the deserialized object to the current schema. The path may be a
single direct multi-version migration, a chain of single-version steps, or a mix of
both; a version gap no path can bridge throws SerializationException
rather than being silently skipped.
Performance. Reflection metadata is cached by TypeMetadataCache so repeated serialization of the same type avoids repeated reflection. Property accessors are compiled to delegates during metadata construction. However, this serializer is inherently slower than hand-written serializers for hot paths; consider implementing ITypeSerializer<T> for types that are serialized frequently.
Constructors
ReflectionSerializer(Type)
Creates a new ReflectionSerializer for the specified type, loading its
TypeMetadata from the global TypeMetadataCache.
Declaration
public ReflectionSerializer(Type type)
Parameters
| Type | Name | Description |
|---|---|---|
| Type | type | The concrete type this serializer will handle. Must not be |
Properties
TargetType
Gets the Type that this serializer is responsible for handling. The engine uses this value as the dictionary key when storing and retrieving serializers from its registry.
Declaration
public Type TargetType { get; }
Property Value
| Type | Description |
|---|---|
| Type | The exact Type this serializer handles. For
TypeSerializerAdapter<T>, this is always |
Methods
ReadObject(IScyllaReader, ISerializationContext)
Deserializes a JSON object from the reader and returns the reconstructed instance.
Handles null tokens, depth limiting, $ref back-references, $type
polymorphism (including $value envelopes), $version schema migrations, unknown-property skipping, and
required-property validation.
Declaration
public object ReadObject(IScyllaReader reader, ISerializationContext context)
Parameters
| Type | Name | Description |
|---|---|---|
| IScyllaReader | reader | The reader positioned at the start of a JSON object or a null token. Never |
| ISerializationContext | context | The active serialization context providing depth tracking, reference tracking,
and settings. Never |
Returns
| Type | Description |
|---|---|
| object | The deserialized object of the type this serializer targets, or |
Exceptions
| Type | Condition |
|---|---|
| SerializationException | Thrown when:
|
WriteObject(object, IScyllaWriter, ISerializationContext)
Serializes value to the writer as a JSON object. Handles
null values, circular reference detection, depth limiting, optional type and
version metadata, null/default value filtering, and property iteration.
Declaration
public void WriteObject(object value, IScyllaWriter writer, ISerializationContext context)
Parameters
| Type | Name | Description |
|---|---|---|
| object | value | The object to serialize, or |
| IScyllaWriter | writer | The writer that receives the serialized output. Never |
| ISerializationContext | context | The active serialization context providing depth tracking, reference tracking,
and settings. Never |
Exceptions
| Type | Condition |
|---|---|
| SerializationException | Thrown when:
|