A most wondrous, lightweight, and local library, fashioned in the native tongue of Lua for the encoding, decoding, stewardship of JSON metadata, file operations, and the great reporting of errors.
Note
Take heed, gentle practitioner! This library abideth wholly in isolation, utterly severed from the global web. Wherefore, whenever a new craft or amendment is brought forth, thou art required to fetch and install it locally upon thy machine. Be not troubled, for no future revision shall break or confound the work thou hast already established.
- One True Scroll: All power dwelleth within
json.luaalone. Superstring and Great Errors are built in — no separate require needed to unlock them. - On File Stewardship: Containeth a companion scroll (
PathFiles.lua) to navigate and govern thy file systems with great ease. - Swift & Unburdened: Wrought in pure Lua, demanding no external alliances nor strange dependencies.
- Secure & Recluse: Absent of all internet commerce, thereby ensuring the absolute privacy of thy data.
| Scroll | Role |
|---|---|
json.lua |
The one true core — encoding, decoding, strict mode, superstring, great errors, and file I/O |
PathFiles.lua |
File and directory stewardship |
superstring.lua |
Thin companion — yes/no mode pre-enabled for convenience |
greaterror.lua |
Thin companion — error display controls for convenience |
lua.mod |
The module manifest |
superstring.luaandgreaterror.luaare companion scrolls, not separate engines. Their powers already live insidejson.lua. They exist purely so thou needst not pass options by hand every time.
1. Deposit the Files into a folder inside thy project, perchance named json:
your-project/
└── json/
├── json.lua
├── PathFiles.lua
├── superstring.lua
├── greaterror.lua
└── lua.mod
2. Invoke the Core at the summit of thy script:
local json = require("json/json")That single line unlocketh everything — encoding, decoding, strict mode, superstring tokens, great errors, and file I/O.
If thou desirest to call json from any script in thy project without writing require("json/json") each time, thou mayest install it as a standard library in one of two ways:
At the very top of thy main entry script, extend Lua's search path:
package.path = package.path .. ";./json/?.lua"
local json = require("json")From this point onward, any script that runs beneath thy main script may call require("json") directly, without a folder prefix.
If thou desirest json to be available everywhere without even calling require, place this at the very top of thy main entry script:
package.path = package.path .. ";./json/?.lua"
json = require("json")Note the absence of local. This maketh json a global name, visible to every script and every module that runs thereafter, as if it were a built-in part of the language itself.
local data = json.decode('{"guild":"TheDevinLabs"}')
print(data.guild)If thou art on Termux or Linux and desirest the library to be available system-wide across all projects:
cp json.lua PathFiles.lua superstring.lua greaterror.lua /data/data/com.termux/files/usr/share/lua/5.4/Or on a standard Linux machine:
sudo cp json.lua PathFiles.lua superstring.lua greaterror.lua /usr/local/share/lua/5.4/After this, any Lua script on the machine may call:
local json = require("json")With no folder, no path extension, no ceremony whatsoever.
local json = require("json/json")
local character_profile = {
username = "CoolyDucks",
guild = "TheDevinLabs",
is_active = true,
projects_count = 3,
languages = { "Lua", "JSON", "Markdown" }
}
local json_parchment = json.encode(character_profile)
print(json_parchment)For a more readable parchment:
local pretty = json.encode(character_profile, { pretty = true, sort_keys = true })
print(pretty)local json = require("json/json")
local raw_scroll = '{"guild": "TheDevinLabs", "status": "Active"}'
local data_table = json.decode(raw_scroll)
print("The Guild Name is: " .. data_table.guild)local profile = { name = "CoolyDucks", level = 42 }
json.encode_file("save.json", profile, { pretty = true })
local loaded = json.decode_file("save.json")
print(loaded.name)For a running journal of events:
json.append_file("log.ndjson", { event = "login", user = "CoolyDucks" })
json.append_file("log.ndjson", { event = "logout", user = "CoolyDucks" })
local entries = json.decode_lines("log.ndjson")
for _, entry in ipairs(entries) do
print(entry.event, entry.user)
endSuperstring mode is built directly into json.lua. Pass { superstring = true } to any encode or decode call:
json.encode({ active = true, banned = false }, { superstring = true })
-- {"active":yes,"banned":no}
json.decode('{"active":yes,"banned":no}', { superstring = true })
-- { active = true, banned = false }
json.decode('[yes, no, true, false]', { superstring = true })
-- { true, false, true, false }Or use the superstring.lua companion, which hath { superstring = true } pre-applied so thou needst not write it each time:
local superstring = require("json/superstring")
superstring.encode({ active = true })
-- {"active":yes}
superstring.decode('{"active":yes}')
-- { active = true }| Token | Decoded As |
|---|---|
true |
true |
false |
false |
yes |
true |
no |
false |
null |
json.null |
null always taketh precedence over no — the parser checketh null first, so there is no ambiguity between the two.
Strict mode is built directly into json.lua. Pass { strict = true }:
json.decode('{"a":1,"a":2}', { strict = true })
-- Great Error: duplicate key "a" not allowed (RFC 8259)
json.decode('01', { strict = true })
-- Great Error: leading zeros not allowed (RFC 8259)
json.encode({ n = 0/0 }, { strict = true })
-- Great Error: non-finite number is not allowed in strict modeBoth modes may be combined:
json.decode('{"enabled":yes,"count":42}', { strict = true, superstring = true })
-- { enabled = true, count = 42 }local ok = json.validate('{"a":1}')
-- true
local ok, err = json.validate('{"a":1,"a":2}')
-- false, "duplicate key..."
json.is_valid_utf8("héllo") -- true
json.is_valid_utf8("\xFF") -- falseWhen things go awry, json.lua speaketh loudly. Instead of a bare nil or a plain Lua error, it printeth to stderr in full colour with source, message, detail, hint, and stack trace:
╔══ ERROR ══════════════════════════════════════╗
║ source : json.decode
║ message : unexpected character "}" at position 13
║ detail : got: "}"
║ hint : check for invalid characters or unquoted strings
║ trace :
║ at myscript.lua:10
╚═══════════════════════════════════════════════╝
This requireth nothing from thee — it simply happeneth whenever a fault is detected.
json.error_set_color(false)
json.error_set_trace(false)
json.error_set_level(json.WARNING)
json.error_reset()error_set_color(false) — disableth ANSI colour codes, useful when writing to a plain log file.
error_set_trace(false) — disableth the stack trace lines.
error_set_level(level) — setteth the minimum severity that will be displayed. Levels are json.FATAL, json.ERROR, json.WARNING, json.INFO.
error_reset() — restoreth all settings to their defaults.
If thou desirest to redirect errors to thy own logging system:
json.error_set_handler(function(level, source, message, detail, hint, formatted)
my_logger.write(level.label .. " [" .. source .. "] " .. message)
end)When a handler is installed, the default stderr output is suppressed entirely.
If thou preferest to configure error display through a dedicated name:
local greaterror = require("json/greaterror")
greaterror.set_color(false)
greaterror.set_trace(false)
greaterror.set_level(greaterror.WARNING)
greaterror.reset()
greaterror.set_handler(function(level, source, message, detail, hint, formatted)
my_logger.write(formatted)
end)This is identical to calling json.error_* — the companion merely giveth it a different name for readability.
| Level | Behaviour |
|---|---|
json.FATAL |
Print Great Error + os.exit(1) |
json.ERROR |
Print Great Error + raise Lua error |
json.WARNING |
Print only, never raise |
json.INFO |
Print only, never raise |
PathFiles.lua governeth all file and directory dealings.
local PathFiles = require("json/PathFiles")local content = PathFiles.read("file.txt")
local lines = PathFiles.read_lines("file.txt")
local bytes = PathFiles.size("file.txt")PathFiles.write("file.txt", "hello world")
PathFiles.append("file.txt", "\nmore words")PathFiles.exists("file.txt")
PathFiles.is_file("file.txt")
PathFiles.is_dir("some/folder")PathFiles.copy("source.txt", "destination.txt")
PathFiles.rename("old.txt", "new.txt")
PathFiles.delete("unwanted.txt")PathFiles.mkdir("path/to/new/dir")
local entries = PathFiles.list("some/folder")PathFiles.join("path", "to", "file.txt") -- path/to/file.txt
PathFiles.basename("path/to/file.txt") -- file.txt
PathFiles.dirname("path/to/file.txt") -- path/to
PathFiles.extension("path/to/file.txt") -- txt
PathFiles.stem("path/to/file.txt") -- file| Situation | Behaviour |
|---|---|
nan / inf in encode |
Encoded as null — Great Error in strict mode |
| Circular reference | Great Error thrown |
| Depth over 512 | Great Error thrown |
nil value in table |
Key skipped silently |
| Mixed table (int + string keys) | Encoded as object |
| Sequential integer keys | Encoded as array |
| UTF-8 strings | Passed through as-is |
\uXXXX escape in decode |
Converted to UTF-8 |
Surrogate pairs \uD800\uDC00 |
Correctly decoded to UTF-8 |
JSON null decoded |
Returns json.null sentinel |
| Trailing garbage | Great Error thrown |
| Duplicate keys in strict mode | Great Error thrown |
| Leading zeros in strict mode | Great Error thrown |
yes / no in superstring mode |
Decoded as true / false |
| Platform | Status |
|---|---|
| Linux x86_64 | ✅ |
| Linux arm64 / Android Termux | ✅ |
| macOS | ✅ |
| Windows | ✅ |
| Lua 5.1 | ✅ |
| Lua 5.2 | ✅ |
| Lua 5.3 | ✅ |
| Lua 5.4 | ✅ |
module json
count as = [library]
include json.lua
include PathFiles.lua
include superstring.lua
include greaterror.lua
This work is bound by the terms of the BSD 2-Clause Simplified License.
- An Open Source: Thou art granted full liberty to employ, alter, and distribute these codes as seest fit.
- A Solemn Disclaimer: Whilst thou mayest use this craft for any noble purpose, thou shalt not covet nor steal the name of this project, nor falsely proclaim it as thine own invention.
Maintained with due diligence by the guild of TheDevinLabs