> 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/all-out-docs/ru/dannye-i-sokhranenie/json.md).

# Сериализация JSON

Для сохранения более сложных структур данных вы можете сериализовать их в JSON и хранить в системе сохранений.

CSL может сериализовать определённые структуры данных в JSON за вас. Это особенно полезно, когда нужно сохранить «набор» связанных полей (например, прогресс игрока, улучшения, разблокировки и т. д.), не управляя множеством отдельных ключей сохранения.

Внутри это использует JSON API системы Save:

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

{% hint style="info" %}
Сохранение/загрузка JSON — **для каждого игрока** (оно принимает `игрока`). Глобальное сохранение игры сейчас поддерживает только строки + int.
{% endhint %}

### Что сохраняется?

Сохраняются только поля, помеченные `@ao_serialize` включаются в JSON.

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

    @ao_serialize xp: s64;
    @ao_serialize level: s64 = 1;

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

### Сохранение JSON

Сохраните всю структуру под одним ключом:

```go
// Примечание: записываются только поля @ao_serialize
Save.set_json(player, "progress", ref progress);
```

В сохранении JSON, `Item_Instance` поля и производные поля предметов сохраняются как выборки из любых постоянных инвентарей этого игрока. Перемещение предмета в другой инвентарь делает его ссылки недействительными, даже для того же игрока. Сохранение предмета вне постоянных инвентарей этого игрока вызывает ошибку. Отсутствующие или ставшие недействительными ссылки и несовпадения производного типа загружаются как `null`. Создайте именованные инвентари перед загрузкой записи. Отдельный `JSON.serialize` не предоставляет такое поведение ссылок на предметы.

### Загрузка JSON (с значениями по умолчанию)

`Save.try_get_json` возвращает `false` если ключ отсутствует или строка JSON повреждена. Выделяйте данные класса перед загрузкой, потому что ветка false не выделяет их. Отсутствующие поля сохраняют значения класса по умолчанию, а неизвестные поля игнорируются.

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

    if !Save.try_get_json(player, "progress", ref progress) {
        // У нового игрока (или при повреждённом JSON) уже есть значения класса по умолчанию.
    }

    return progress;
}
```

{% hint style="warning" %}
Изменение сохранённого поля на несовместимый тип — недопустимый ввод, и это может завершиться громкой ошибкой. Используйте новое поле или ключ сохранения, когда старое значение нельзя безопасно преобразовать.
{% endhint %}

### Версионирование и миграции

Если вы ожидаете, что схема JSON изменится, включите `version` поле в структуру и выполняйте миграцию после загрузки.

Лучшие практики для сохранения совместимости:

* **Предпочитайте добавлять новые поля** с разумными значениями класса по умолчанию.
* Сохраняйте поле версии, когда новое значение должно вычисляться из старых данных.
* Если вам нужен жёсткий разрыв, рассмотрите возможность сохранения под **новым ключом** (например, `"progress_v2"`) и сохранения запасного загрузчика.

Пример миграции:

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

    if !Save.try_get_json(player, "progress", ref progress) {
        // Значения класса по умолчанию уже описывают нового игрока.
    }

    // Мигрируйте вперёд
    if progress.version < 2 {
        progress.version = 2;
        // Пример: в версии 2 добавлено unlocked_skins (инициализируйте его)
        progress.unlocked_skins = .{};
    }

    // Запишите обратно мигрированную версию
    Save.set_json(player, "progress", ref progress);
    return progress;
}
```

### Фиксированные массивы

Ввод для массива фиксированного размера ограничивается назначением:

* Лишние элементы JSON игнорируются.
* Отсутствующие элементы оставляют остальные элементы назначения без изменений.

### Когда использовать JSON, а когда простые ключи

* Используйте **простые ключи** (`Save.set_int`, `Save.set_string`, и т. д.) для небольшого числа значений, которые вы часто читаете/записываете.
* Используйте **JSON** когда нужно хранить целостную структуру (прогресс, наборы снаряжения, разблокировки) и держать логику сохранения централизованной.

### Отдельный JSON API

Вы также можете сериализовать/десериализовать JSON независимо от системы Save (например, для логирования, сетевого взаимодействия или собственного хранилища):

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