Appearance
json
Turns a table into text, and text back into a table. A store holds text; this is how a table gets into one.
luau
local json = require("json")encode
luau
json.encode(value) -> text | nil, reasonluau
local text = json.encode({ coins = 10, items = { "axe", "rope" } })
-- '{"coins":10,"items":["axe","rope"]}'Called with a dot, not a colon — these two functions do not belong to a thing, they just convert.
Named fields always come out sorted, so encoding the same table twice gives the same text. That is what lets you compare two saves as plain strings and trust the answer.
decode
luau
json.decode(text) -> value | nil, reasonluau
local back = json.decode(self.store:get("save") or "{}")
print(back.coins) -- 10, a numberWhole numbers stay whole: an id you saved comes back as the same digits, not as 1.0e15.
Both refuse instead of guessing
Neither raises. You get nil and a reason, so a script handed bad text can carry on:
luau
local value, why = json.decode(text)
if not value then
print("could not read the save: " .. why)
value = {} -- start fresh
endencode refuses when:
| what | reason |
|---|---|
| the table contains itself | cannot encode a table that contains itself |
a table mixes a list and named fields — { 1, 2, name = "x" } | cannot encode a table that mixes list and record |
a number is nan or infinity | JSON has no way to write them |
| a string is not valid text | cannot encode a string that is not valid text (UTF-8) |
| over 256 KB, or nested over 64 deep | it says which |
decode refuses malformed text and says where:
luau
json.decode('{"a":1} leftover')
-- nil, "there is leftover text after the value (at position 9)"Leftover text is an error rather than a silent half-read — that is the shape of two saves written over each other, and reading the first half as if it were the whole thing is how a corrupted save looks fine until it doesn't.
Three things worth knowing
A list must not contain null. [1, null, 3] is refused: a Lua table with a hole is not a list, and # would lie about it.
null in a record means absent. {"a": null} decodes to a table with no a — Lua has no way to hold "present but nil".
An empty list comes back as an empty record. [] encodes again as {}, because in Lua an empty list and an empty record are the same value — #t is 0 and next(t) is nil for both. It is stable: encode it again and it stays {}, so a save that round-trips twice never drifts.
Example: a save that survives anything
luau
--!strict
local self = require("self")
local json = require("json")
type Save = { owner: string, uses: number, notes: { string } }
local function load(): Save
local raw = self.store:get("state")
if not raw then return { owner = "", uses = 0, notes = {} } end
local value = json.decode(raw)
if not value then return { owner = "", uses = 0, notes = {} } end
return value :: Save
end
local function save(s: Save)
local text, why = json.encode(s)
if not text then
print("could not save: " .. why)
return
end
local ok, reason = self.store:set("state", text)
if not ok then print("could not save: " .. reason) end
endBoth refusals are checked, and neither can take the script down.
See also
- Saving data — the recipe, including the world's own store
- world.store — memory shared by every script in the region
