# Custom serializers

A **Serializer** converts the save snapshot to text and back. Visual Script includes Unity JSON; a custom subclass adds another format to [Save settings](../../../manual/save-load/settings.md).

## The interface

Derive from `SerializerBase` and implement the round trip. This outline omits the format-specific conversion:

```cs
[Title("My Serializer")]
[Serializable]
public class SerializerMyFormat : SerializerBase
{
    public override string Serialize(object value) { /* … */ }
    public override object Deserialize(string data, Type type) { /* … */ }
}
```

The save system passes the expected `Type` to `Deserialize`, because the data may not carry enough information to reconstruct it.

## When to write one

Use a custom serializer when the stored format needs to satisfy one of these requirements:

| Reason | Use when |
| :--- | :--- |
| **Size** | A compact format meaningfully reduces large saves. |
| **Interoperability** | An external tool or server needs to read the data. |
| **Polymorphism** | You need type handling Unity's serializer doesn't offer. |
| **Migration** | Reading an older format written by a previous system. |

Use the built-in serializer unless the project has a specific format requirement. Changing formats affects every existing save.

!!! warning "Changing the serializer invalidates existing saves"
    A serializer can't read an existing save unless it supports that save's format. Switching formats without a migration path makes existing saves unreadable, including those used for testing.

    If you must switch after shipping, keep the old serializer available and detect which format a save is in before choosing one.

## Requirements

The format must support the values the save system stores:

- **Unity types** — vectors, quaternions, colors, and the framework's own structs.
- **Polymorphic data** — Memories and variable values are stored through base-class references, so the concrete type must survive the round trip.
- **Nested objects and arrays**, several levels deep.
- **Null values**, both in fields and array entries.

Check polymorphic data before choosing a library. A serializer that writes only `{"x":1,"y":2}` for a base-class reference loses the concrete type, so loading can produce the wrong type or no object.

## Determinism

Prefer a serializer that produces the same output for the same input. This makes it easier to compare two snapshots during debugging because changes in the text reflect changes in saved state.

When dictionary-key order varies between writes, comparisons can show differences even when the saved values haven't changed.

## Testing

Round-trip the representative and failure cases:

- An empty save.
- A save containing every memory type.
- A save containing every variable value type.
- Nulls in arrays and object fields.
- Deeply nested data.
- Text with quotes, newlines, and non-ASCII characters.
- A save written by a previous version of your game.

The previous-version case detects format changes before they invalidate shipped saves.

## Interaction with encryption

Serialization happens first, then [encryption](encryption.md). Your serializer always sees plain data and never needs to know whether encryption is enabled.

## Where to go next

- **[Custom storage](storage.md)** — write the serialized text.
- **[Custom encryption](encryption.md)** — transform the text after serialization.
- **[Save settings](../../../manual/save-load/settings.md)** — select a serializer.
