xiao.json New
xiao.json converts Lua values to JSON text and JSON text to Lua values.
It is pure computation: it touches no file and no game state, so it works everywhere, including inside a packet hook or a cmd hook (mind their 2 ms budget on big tables, see Execution contexts). Every failure raises an error that says what and where: wrap calls in pcall when the input comes from outside.
To keep a table on disk across runs, xiao.workspace does the file handling and uses this encoder, with the same
At a glance
Conversion rules
- An empty table is an object
{}by default. Usexiao.json.array()for[]. - Object keys must be strings. A number key outside a
1..nsequence ({[5] = true}, a table with a hole,{[1.5] = 1}) raises an error: convert it withtostringfirst. - Integers in a JSON text beyond
2^53are rounded to the nearest double, like every Lua 5.1 number. - A duplicate key in a JSON object keeps the last value.
nilcannot live in a Lua table: usexiao.json.nullto keep anullin an array or an object.- Metatables are ignored, except the
xiao.json.arraymark. Vectors and colors become objects ({"x":1,"y":2}).
Refused values
Functions, coroutines, userdata other than xiao.json.null, NaN, infinities, strings that are not valid UTF-8 and tables that contain themselves (cycles) raise an error. A table used twice without a cycle is fine: it is written twice.
Functions
xiao.json.encode(value, opts): string
xiao.json.encode(value: any, opts?: {pretty?: boolean, indent?: integer}): string
Returns the JSON text of value. The output is compact (no spaces) unless opts asks for pretty printing.
Returns: the JSON text.
Errors: every message starts with json encode: and ends with the path of the faulty value, such as json encode: cannot encode a function at $.hooks[2]:
cannot encode a function,a threadora userdata;cannot encode NaN,cannot encode infinitystring is not valid UTF-8,object key is not valid UTF-8object key must be a string, got number 5xiao.json.array table has keys other than 1 to ncycle detected,nested deeper than 128 levels,more than 1048576 valuesencoded text is 9000000 bytes, the limit is 8388608, without a path
A bad opts raises bad argument #2 to 'encode' (...) with unknown option 'name', option names must be strings, pretty must be a boolean or indent must be an integer from 1 to 8.
local json = xiao.json
print(json.encode({name = "steve", hp = 20, tags = {"pvp", "afk"}}))
print(json.encode({a = {1, 2}}, {pretty = true}))
{"hp":20,"name":"steve","tags":["pvp","afk"]}
{
"a": [
1,
2
]
}
xiao.json.decode(text): any
xiao.json.decode(text: string): any
Parses a complete JSON text and returns its value. Standard JSON only: no comments, no trailing comma, nothing after the value. A UTF-8 byte order mark at the start is skipped.
Returns: the value, see null gives xiao.json.null, arrays are marked with xiao.json.array.
Errors: every message starts with json decode: .
- a syntax error gives the line and the column:
json decode: parse error at line 1, column 8: syntax error while parsing object key - unexpected '}'; expected string literal number overflow parsing '1e400'for a number out of the double rangenested deeper than 128 levelstext is 9000000 bytes, the limit is 8388608
local ok, value = pcall(xiao.json.decode, '{"x": 1, "list": [1, null, 3]}')
if ok then
print(value.x, #value.list, value.list[2] == xiao.json.null)
else
print("bad json:", value)
end
xiao.json.array(t): table
xiao.json.array(t?: table): table
Marks t (or a new empty table) as a JSON array and returns it. A marked table is always written as an array, [] when empty, and decode marks every array it creates, so a decoded [] encodes back as [].
Returns: t, or a new empty marked table.
Errors: bad argument #1 to 'array' (table already has a metatable). Marking a table twice is fine. Encoding a marked table with keys other than 1..n raises xiao.json.array table has keys other than 1 to n.
print(xiao.json.encode({items = xiao.json.array()}))
{"items":[]}
xiao.json.null
xiao.json.null: userdata
The JSON null. Compare with ==: value == xiao.json.null. tostring(xiao.json.null) is "xiao.json.null". Encoding nil also gives null, but only xiao.json.null can be stored in a table.
Complete example
Keep a list of seen players in a file and reload it on the next run (see xiao.fs, or xiao.workspace which does the same with less code).
local json, fs = xiao.json, xiao.fs
local path = "data/seen.json"
local seen = {}
if fs.isfile(path) then
local ok, value = pcall(json.decode, fs.read(path))
if ok and type(value) == "table" then seen = value end
end
xiao.signal.add("entity_add", "seen", function(entity)
if entity.is_player then
seen[entity.name] = {last = os.date("%Y-%m-%d %H:%M"), x = entity.x, z = entity.z}
end
end)
xiao.signal.add("unload", "seen", function()
fs.makefolder("data")
fs.write(path, json.encode(seen, {pretty = true}))
end)