> For the complete documentation index, see [llms.txt](https://docs.allout.game/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.allout.game/data-and-persistence/json.md).

# JSON Serialization

CSL can serialize certain data structures into JSON for you. This is most useful when you want to save a “bundle” of related fields (like player progress, upgrades, unlocks, etc) without managing lots of separate save keys.

Under the hood, this uses the Save system's JSON APIs:

* `Save.set_json(player, key, value)`
* `Save.try_get_json(player, key, out) -> bool`

{% hint style="info" %}
JSON save/load is **per-player** (it takes a `player`). Game-wide save currently supports strings + ints only.
{% endhint %}

### What gets saved?

Only fields marked with `@ao_serialize` are included in the JSON.

```go
Player_Progress :: class {
    version: s64 @ao_serialize;

    xp: s64 @ao_serialize;
    level: s64 @ao_serialize;

    unlocked_skins: [..]string @ao_serialize;
}
```

### Saving JSON

Save the whole structure under one key:

```go
// Note: Only @ao_serialize fields are written
Save.set_json(player, "progress", ref progress);
```

### Loading JSON (with defaults)

`Save.try_get_json` returns `false` if the key is missing or the JSON string is malformed. If the key exists but the structure has changed (new fields added, old fields removed), it returns `true` with missing fields zero-filled.

```go
load_progress :: proc(player: Player) -> Player_Progress {
    progress: Player_Progress;

    if !Save.try_get_json(player, "progress", ref progress) {
        // New player (or old JSON no longer matches) → defaults
        progress.version = 1;
        progress.xp = 0;
        progress.level = 1;
        progress.unlocked_skins = .{};
    }

    return progress;
}
```

{% hint style="warning" %}
If you rename/remove serialized fields, old JSON may fail to parse and you'll drop into your defaults path. Plan migrations early (see below).
{% endhint %}

### Versioning & migrations

If you expect your JSON schema to change, include a `version` field in the structure and migrate after load.

Best practices for staying compatible:

* **Prefer adding new fields** (old JSON often still parses; fill in defaults after load)
* **Avoid renaming/removing fields** (can cause parse failures)
* If you need a hard break, consider saving under a **new key** (e.g. `"progress_v2"`) and keeping a fallback loader

Migration Example:

```go
load_progress_and_migrate :: proc(player: Player) -> Player_Progress {
    progress: Player_Progress;

    if !Save.try_get_json(player, "progress", ref progress) {
        progress.version = 1;
        progress.xp = 0;
        progress.level = 1;
        progress.unlocked_skins = .{};
    }

    // Migrate forward
    if progress.version < 2 {
        progress.version = 2;
        // Example: v2 introduced unlocked_skins (initialize it)
        progress.unlocked_skins = .{};
    }

    // Write back the migrated version
    Save.set_json(player, "progress", ref progress);
    return progress;
}
```

### When to use JSON vs simple keys

* Use **simple keys** (`Save.set_int`, `Save.set_string`, etc) for a handful of values you read/write often.
* Use **JSON** when you want to store a cohesive structure (progress, loadouts, unlocks) and keep your save logic centralized.

### Standalone JSON API

You can also serialize/deserialize JSON independently of the Save system (e.g. for logging, networking, or custom storage):

```go
JSON :: struct {
    serialize       :: proc(obj: ref $T) -> string;
    try_deserialize :: proc(json: string, out_value: ref $T) -> bool;
}
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.allout.game/data-and-persistence/json.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
