← All Modules

json

JSON serialization. No require() needed.

Empty tables

Lua has one composite type covering both arrays and objects, so the encoder has to pick a shape for {}. The default is object — json.encode({}) returns "{}". To express an empty JSON array, use json.array({}) (or just json.array()) which tags the table via a __jsontype = "array" metatable marker that the encoder honours regardless of contents.

json.encode({})                 -- "{}"
json.encode(json.array({}))     -- "[]"
json.encode(json.array({1,2}))  -- "[1,2]"
json.encode(json.object({"a"})) -- '{"1":"a"}'  (object with stringified key)

For non-empty tables the heuristic still applies: contiguous 1..N integer keys encode as arrays, anything else as objects. The helpers only matter when you need to override that.

yaml

YAML serialization. No require() needed.

Tagged nodes

A local or custom tag — !reference, !custom, !Ref, anything the YAML core schema does not resolve — becomes a one-key table keyed by the tag text, bang included, with the payload converted recursively. A scalar, a sequence and a mapping all wrap the same way.

local data = yaml.parse("job:\n  script:\n    - !reference [.anchor, script]\n")
data.job.script[1]["!reference"] -- { ".anchor", "script" }

Standard tags resolve to their scalar meaning rather than being wrapped, in both modes: !!str 1 is the string "1", !!int "5" is 5, and !!bool, !!float and !!null behave the same, as do the tag:yaml.org,2002: long forms. !!binary and !!timestamp yield the scalar text as written — neither is decoded or parsed.

A tag over an empty payload (a: !foo) wraps with the empty string, because a Lua table cannot hold a nil value and the tag would otherwise be lost.

opts.tags on yaml.parse and yaml.parse_all picks the shape:

yaml.parse("a: !custom {x: 1}\n") -- { a = { ["!custom"] = { x = 1 } } }
yaml.parse("a: !custom {x: 1}\n", { tags = "strip" }) -- { a = { x = 1 } }

yaml.encode takes the same option and defaults to "strip": a key starting with ! is an ordinary mapping key unless you ask for tags. Pass { tags = "wrap" } to turn a one-key table whose sole key starts with ! back into a tagged node, which is what round-trips a wrapped parse.

yaml.encode({ ["!reference"] = { ".anchor", "script" } }) -- "'!reference':\n- '.anchor'\n- script\n"
yaml.encode({ ["!reference"] = { ".anchor", "script" } }, { tags = "wrap" })
-- "!reference\n- '.anchor'\n- script\n"

Any other value for tags is a runtime error, in either direction.

Untagged documents

Nothing about an untagged document changed when tags were understood, and that is enforced by test. Mapping keys arrive as the text the document wrote — 9000, ~, TRUE and 1.50 are the Lua string keys "9000", "~", "TRUE" and "1.50" — a repeated key keeps its last value, anchors and aliases expand, a << merge key stays a literal key, and .inf / .nan are nil.

toml

TOML serialization. No require() needed.