FiveM json.decode ошибка: nil, пустая строка и исправления невалидного JSON

json.decode падает или возвращает nil в вашем FiveM скрипте? Почему декодирование nil, пустых или отредактированных вручную JSON данных ломается, что делает json.encode с смешанными ключами, и как безопасно использовать pcall.

Ошибка появляется в строке после декодирования:

text
SCRIPT ERROR: @my_script/server/main.lua:31: attempt to index a nil value (local 'data')
lua
local data = json.decode(row.metadata)
print(data.level)   -- data is nil

json.decode дал вам ничего полезного, поэтому следующая строка индексировала nil. Декодирование редко является настоящей причиной. Входная строка была nil, пустая или нарушена, или таблица, которую вы сохранили ранее, была не тем, что вы думали. Вот как найти что было не так, и как декодировать безопасно.

1. Строка это nil или пустая

Самый распространённый случай. Значение, которое вы декодируете, ещё не существует:

  • Столбец базы данных это NULL. Строка без сохранённых данных возвращает nil для этого столбца.
  • Запрос без результата. MySQL.scalar.await(...) и MySQL.single.await(...) возвращают nil когда ни одна строка не совпадает. Смотрите oxmysql руководство запросов для того что возвращает каждая функция.
  • KVP ключ, который никогда не был установлен. GetResourceKvpString('my_key') возвращает nil когда ключ не существует, смотрите FiveM resource KVP.
  • Файл, который отсутствует. LoadResourceFile(GetCurrentResourceName(), 'data.json') возвращает nil когда файл там нет.
  • Пустая строка. Столбец сохранённый как '' тоже не является валидным JSON.

Декодирование nil или '' это ошибка вызывающей стороны, поэтому проверьте строку сначала:

lua
local raw = GetResourceKvpString('my_key')
local data = {}

if raw and raw ~= '' then
    data = json.decode(raw) or {}
end

2. JSON невалидный

Строка, которая присутствует, может быть всё ещё невалидной. Типичные причины когда файл или значение базы данных было отредактировано вручную:

  • Завершающая запятая после последней записи: { "a": 1, }
  • Одинарные кавычки вместо двойных: { 'a': 1 }
  • Комментарии в файле. JSON не допускает их.
  • Отсутствующая скобка или кавычка после копирования части файла.
  • Синтаксис Lua вставленный в JSON файл, такой как a = 1 или [1] = 'x'.
  • Нарушенный символ из текстового редактора, такой как кривые кавычки скопированные из веб-страницы.
json
{
  "items": [
    { "name": "water", "count": 2 },
    { "name": "bread", "count": 1 }
  ]
}

Валидируйте файл перед тем как винить скрипт. VS Code подчёркивает JSON ошибки во время ввода, и онлайн JSON валидатор показывает точную позицию ошибки.

3. Всегда декодируйте с pcall

В зависимости от входной строки и сборки, плохая строка либо вызывает Lua ошибку, либо возвращается как nil. Вам не нужно помнить какая: оберните вызов чтобы обработать оба случая.

lua
local function safeDecode(str)
    if type(str) ~= 'string' or str == '' then
        return nil
    end

    local ok, result = pcall(json.decode, str)
    if not ok or type(result) ~= 'table' then
        return nil
    end

    return result
end

local data = safeDecode(row.metadata) or {}
print(data.level)

Проверка type(result) ~= 'table' также ловит значение, которое является валидным JSON но не объектом, такой как "hello" или 5.

Совет: когда декодирование падает и вы не знаете почему, выведите сырую строку рядом с её типом: print(type(str), str). nil или пустая строка показывает причину сразу.

4. json.encode и ключи, которые меняются

Декодирование может работать и всё ещё дать вам неправильные данные, потому что таблица была сохранена в форме, которая не переживает путешествие туда и обратно. JSON имеет две формы: список [...] и объект {...} с текстовыми ключами.

lua
local saved = json.encode({ [100] = 'a', [250] = 'b' })
local loaded = json.decode(saved)

print(loaded[100])     -- nil
print(loaded['100'])   -- 'a'

Что это значит на практике:

  • Числовые ключи становятся текстовыми ключами когда таблица сохраняется как объект. После декодирования, ищите значение с текстовым ключом, или конвертируйте ключ с tonumber.
  • Разреженные массивы с дырами ({ [1] = 'a', [3] = 'c' }) не являются чистым списком, поэтому они могут быть написаны как объект вместо массива.
  • Смешанные таблицы, как с элементами списка так и с именованными ключами, не отображаются чисто ни на одну форму. Держите список как список, и поместите именованные поля в отдельную таблицу.
  • Функции, userdata и vectors не могут быть написаны как обычные JSON данные. Преобразуйте vector в { x = v.x, y = v.y, z = v.z } перед тем как вы закодируете его.
  • Пустая таблица обычно написана как [], поэтому читайте её обратно как список или пустую таблицу, не как объект.

Безопасный способ сохранения координат:

lua
-- save
local payload = json.encode({ x = coords.x, y = coords.y, z = coords.z })

-- load
local pos = safeDecode(payload)
if pos then
    SetEntityCoords(ped, pos.x, pos.y, pos.z, false, false, false, false)
end

Используйте json.encode(value, { indent = true }) во время отладки, так как это намного легче читать.

5. Декодируйте один раз, и проверьте тип

Данные из базы данных могут дойти до вашего скрипта как строка в одном месте и как таблица в другом. Некоторые библиотеки и фреймворки декодируют JSON колонки для вас, другие возвращают сырой текст. Перед тем как вы декодируете, проверьте что вы держите:

lua
local meta = row.metadata
if type(meta) == 'string' then
    meta = safeDecode(meta)
end
meta = meta or {}

Декодирование чего-то, что уже является таблицей, это ошибка, и проверка типа ничего не стоит.

Контрольный список

Симптом Решение
attempt to index a nil value (local 'data') после декодирования Строка была nil или невалидной; проверьте её перед декодированием и используйте pcall
Декодирование столбца базы данных NULL и результаты без строк это nil; охраните с if raw and raw ~= ''
Чтение KVP дает ничего Ключ никогда не был установлен; используйте таблицу по умолчанию
Отредактированный вручную файл падает Удалите завершающие запятые, комментарии и одинарные кавычки; валидируйте его в редакторе
Числовые ключи потеряны после декодирования JSON ключи это текст; читайте t['100'] или конвертируйте с tonumber
Vectors написаны как JSON Сохраняйте x, y и z как отдельные поля

Короткие ответы

Почему json.decode возвращает nil или выбрасывает ошибку?

Строка это nil, пустая, или невалидный JSON. В зависимости от случая это либо вызывает ошибку, либо ничего не возвращает, поэтому оберните вызов в pcall и проверьте результат.

Почему мои числовые ключи становятся строками после json.decode?

JSON ключи объектов всегда текст. Таблица вроде { [100] = true } сохраняется как объект с текстовым ключом 100 и возвращается с ключом '100', поэтому data[100] это nil.

Как я могу проверить что JSON файл валидный?

Откройте его в VS Code, которые помечает ошибки JSON в редакторе, или вставьте в онлайн JSON валидатор. Обычные ошибки — это завершающая запятая, одинарные кавычки или комментарий.

Скрипты без этой проблемы

Item Creator V2Создавайте используемые предметы с анимациями, пропами, эффектами и не только — без кода.Смотреть скрипт →Shop CreatorМагазин меньше чем за минуту — владельцы, сотрудники, сейфы и ограбления в комплекте.Смотреть скрипт →Quest CreatorВизуальный редактор квестов и диалогов с NPC — узел за узлом прямо в игре.Смотреть скрипт →

Читайте также