Serialization
The Serialization module covers four separate concerns: file-backed JSON data, serializable type metadata, polymorphic object snapshots, and process-local object handles. Choose the smallest API that matches the lifetime of the data.
Save Files
SaveLoadSerializer converts an object to JSON, then delegates the JSON string
to an ISerializeFormatter that reads or writes a stream.
using System;
using Ceres.Serialization;
using UnityEngine;
[Serializable]
public sealed class PlayerProfile
{
public int level;
public string displayName;
}
var serializer = new SaveLoadSerializer(
Application.persistentDataPath,
"json",
TextSerializeFormatter.Instance);
serializer.Serialize("profile", new PlayerProfile
{
level = 12,
displayName = "Ceres"
});
PlayerProfile profile = serializer.DeserializeOrNew<PlayerProfile>("profile");
The serializer exposes keyed Serialize, Deserialize, DeserializeOrNew,
Overwrite, Exists, Delete, and DeleteAll operations. TryDeserialize
reads the formatted JSON string without constructing an object.
Objects use Unity JsonUtility by default. Apply PreferJsonConvertAttribute
to a class or interface to use Newtonsoft.Json instead:
using System;
using System.Collections.Generic;
using Ceres.Serialization;
[Serializable]
[PreferJsonConvert]
public sealed class InventoryState
{
public Dictionary<string, int> counts = new();
}
The built-in stream formatters are:
| Formatter | Contract |
|---|---|
TextSerializeFormatter |
Reads and writes UTF-8 JSON text. |
BinarySerializeFormatter |
Stores the JSON string through .NET BinaryFormatter. |
EncryptedSerializeFormatter |
Encrypts the JSON string with password-derived AES-CBC. |
Implement ISerializeFormatter when storage needs a different stream encoding.
The formatter does not choose the JSON serializer.
EncryptedSerializeFormatter does not expose authenticated encryption. If save
data integrity matters, add project-owned authentication or validation.
SaveUtility
SaveUtility is the fixed project save facade. It writes .sav files below
SaveUtility.SavePath with BinarySerializeFormatter:
SaveUtility.Save(new PlayerProfile { level = 4 });
PlayerProfile profile = SaveUtility.LoadOrNew<PlayerProfile>();
bool exists = SaveUtility.Exists(nameof(PlayerProfile));
On Windows and in the Editor, SavePath is the project's Saved directory. On
other Players it is Application.persistentDataPath/Saved.
Serializable Type References
SerializedType<T> stores assembly-qualified type metadata while constraining
the selected type to T. It is suitable for strategy, provider, formatter, or
module types selected in the Inspector.
using System;
using Ceres.Serialization;
public interface ISpawnPolicy
{
void Apply();
}
[Serializable]
public sealed class DefaultSpawnPolicy : ISpawnPolicy
{
public void Apply() { }
}
SerializedType<ISpawnPolicy> policyType =
SerializedType<ISpawnPolicy>.FromType(typeof(DefaultSpawnPolicy));
Type runtimeType = policyType.GetObjectType();
ISpawnPolicy policy = policyType.GetObject();
GetObject() constructs and caches an instance with Activator.CreateInstance,
so the selected concrete type must be constructible without arguments.
GetObjectType() resolves only the type. IsValid() reports whether the stored
metadata currently resolves.

The non-generic SerializedType helper converts types to and from the compact
metadata format used by Ceres and supplies the generic-port utilities used by
Graph and Flow. FormerlySerializedTypeAttribute and
SerializedTypeRedirector.RedirectSerializedType are explicit type-resolution
hooks; no redirect is created automatically.
Polymorphic Object Snapshots
SerializedObject<T> stores the concrete type and a Unity JSON snapshot behind
a base type or interface:
using System;
using Ceres.Serialization;
public interface IEffect
{
void Apply();
}
[Serializable]
public sealed class DamageEffect : IEffect
{
public float amount;
public void Apply() { }
}
SerializedObject<IEffect> serialized =
SerializedObject<IEffect>.FromObject(new DamageEffect { amount = 25f });
IEffect cached = serialized.GetObject();
IEffect independentCopy = serialized.NewObject();
GetObject() caches the first deserialized instance. NewObject() creates a
fresh instance from the stored snapshot. CloneT() copies the serialized data,
not the cached object.
The payload always uses JsonUtility; PreferJsonConvertAttribute does not
affect SerializedObject<T>. The usual Unity JSON restrictions apply, including
field-based serialization and no general-purpose Unity object graph support.
Process-local Object Handles
SoftObjectHandle identifies an object registered with GlobalObjectManager.
The handle contains a sparse-array index and serial number, so a removed slot
cannot resolve a newer object that later reuses the same index.
object value = new object();
var handle = new SoftObjectHandle(value);
object resolved = handle.GetObject();
GlobalObjectManager.UnregisterObject(handle);
These handles are valid only for the current process and registry lifetime.
GlobalObjectManager.Cleanup() invalidates all existing handles and raises
OnGlobalObjectCleanup. The manager holds registered objects until they are
unregistered or the registry is cleaned.
Use Tools > Ceres > Debug > Serialization Debugger to inspect the live registry in the Editor.
Related API: SaveLoadSerializer, SerializedType<T>, SerializedObject<T>, and GlobalObjectManager.