5.9 KiB
utils
The utils module is a grab-bag of helpers that the rest of the standard
library leans on: JSON encoding and decoding, a null marker for round-tripping
JSON and SQL, error-trapping wrappers, and a value pretty-printer. It is always
available as the global utils.
local data = utils.fromJSON('{"name":"Alice","age":30}')
print(data.name, data.age) --> Alice 30
print(utils.toJSON({1, 2, 3})) --> [1,2,3]
print(utils.dump({a = 1, b = {2, 3}})) --> { a = 1, b = { 2, 3 } }
JSON
utils.toJSON(value [, pretty])
Serialize a Lua value to a JSON string. Pass pretty = true for indented,
multi-line output; the default is compact.
utils.toJSON({ name = "test", count = 5 }) --> {"count":5,"name":"test"}
utils.toJSON({ name = "test" }, true) --> pretty-printed over several lines
How values are converted:
| Lua | JSON |
|---|---|
nil |
null |
utils.NULL |
null |
boolean |
true / false |
| integer | number |
| float | number |
string |
string |
| table (sequence) | array |
| table (other) | object |
A table is encoded as a JSON array when its keys are exactly the integers
1..#t with no gaps, and as a JSON object otherwise. Object keys must be
strings or integers (an integer key becomes its decimal string); any other key
type is an error.
These cases raise an error rather than producing invalid JSON:
NaNor infinity (JSON has no representation for them),- a value that has no JSON form (function, coroutine, userdata other than
utils.NULL), - nesting deeper than 64 levels.
utils.fromJSON(string)
Parse a JSON string into a Lua value. The inverse of toJSON:
| JSON | Lua |
|---|---|
null |
utils.NULL |
true / false |
boolean |
| integer number | integer |
| fractional number | float |
| string | string |
| array | sequence table |
| object | table |
Invalid JSON raises an error. Note that JSON null decodes to
utils.NULL, not Lua nil — so a null inside an array does
not create a gap in the sequence.
local arr = utils.fromJSON('[1, null, 3]')
print(#arr) --> 3
print(utils.isNull(arr[2])) --> true
null
JSON and SQL both have a null/NULL value distinct from "absent". Lua's nil
cannot fill that role: a table cannot store nil (assigning nil deletes the
key), so a literal nil can never survive inside a table to reach a JSON array
slot or a SQL parameter. utils.NULL is a sentinel that can.
utils.NULL
A unique marker standing for an explicit null. Store it in a table where you mean "null, not missing":
http.postJSON(url, { nickname = utils.NULL }) -- sends {"nickname":null}
con:execute("UPDATE u SET nickname = ?", { utils.NULL }) -- binds SQL NULL
It is the value fromJSON produces for JSON null, the value toJSON and the
http/sqlite modules turn back into null/NULL. Comparable by identity, so
value == utils.NULL works, but prefer utils.isNull for clarity.
utils.isNull(value)
Return true if value is utils.NULL. Everything else, including Lua nil,
returns false.
utils.isNull(utils.NULL) --> true
utils.isNull(nil) --> false
utils.isNull(0) --> false
Error handling
utils.try(fn [, ...])
Call fn and trap any error, returning (result, nil) on success or
(nil, err) on failure — a more ergonomic pcall for the common
single-return-value case. Extra arguments are forwarded to fn.
local data, err = utils.try(function()
return utils.fromJSON(input)
end)
if not data then
log.error("bad JSON: " .. tostring(err))
return
end
-- use data
If fn errors with a nil value, err is the string "unspecified error", so
a falsy first return reliably means failure.
utils.tryn(n, fn [, ...])
Like try, but for a function that returns several values. n is how many
values fn returns on success. On success it returns those n values followed
by a trailing nil (the error slot); on failure it returns n nils followed
by the error.
-- fn returns two values
local x, y, err = utils.tryn(2, function()
return parsePoint(s) -- returns x, y
end)
if err then
log.error("parse failed: " .. tostring(err))
else
print(x, y)
end
n must be an integer between 0 and 64. Use try when fn returns at most one
value; reach for tryn only when you genuinely need to capture several.
Debugging
utils.dump(value)
Render any Lua value as a readable string — handy for logging and quick
inspection. Strings are quoted, sequences print as { a, b, c }, other tables as
{ key = value, ... }, and utils.NULL prints as null. Cycles are shown as
<circular> rather than looping forever, and nesting is capped at 20 levels.
print(utils.dump({ id = 1, tags = {"a", "b"}, parent = utils.NULL }))
--> { id = 1, parent = null, tags = { "a", "b" } }
This is for human eyes, not machine parsing — use toJSON when you need output
you can read back.
Notes
utilsis always loaded. Norequire; the global is present in every script.NULLbridges three worlds. The same marker represents null fortoJSON,fromJSON, thehttpJSON helpers, and SQLNULLbinding insqlite, so a null value can round-trip through any of them.toJSON/fromJSONare not symmetric fornil.nilencodes tonull, butnulldecodes toutils.NULL. This is deliberate, so decoding never silently drops array elements.