在 Lua 中实现 JSON 与 Table 的相互转换是常见的数据序列化需求。以下是详细的实现方案、性能优化技巧及进阶用法:
库名称 | 特性 | 性能 | 适用场景 |
---|---|---|---|
dkjson | 纯 Lua 实现,兼容性好,支持 UTF-8,但性能较低 | 中低 | 嵌入式项目、小型数据 |
lua-cjson | C 扩展实现,性能极高,支持复杂类型(如二进制数据) | 极高 | 高性能场景(游戏服务器、API) |
dkjsonx | dkjson 扩展版,支持更严格的 JSON 格式校验 | 中 | 需要严格 JSON 合规性的场景 |
lua-cjson
(高性能 C 库)luarocks install lua-cjson
local cjson = require "cjson"
-- Table → JSON
local tbl = {name="Alice", age=30, hobbies={"reading", "coding"}}
local json_str = cjson.encode(tbl)
print(json_str) -- {"age":30,"hobbies":["reading","coding"],"name":"Alice"}
-- JSON → Table
local new_tbl = cjson.decode(json_str)
print(new_tbl.name) -- Alice
-- 启用严格模式(禁止 NaN/Infinity)
cjson.encode_sparse_array(false) -- 不允许稀疏数组
cjson.encode_max_depth(100) -- 设置最大递归深度
dkjson
(纯 Lua 实现)luarocks install dkjson
local json = require("dkjson")
-- Table → JSON
local tbl = {status="ok", data={id=1, value=100}}
local json_str = json.encode(tbl, { indent = true }) -- 美化输出
print(json_str)
--[[
{
status = "ok",
data = {
id = 1,
value = 100
}
}
]]
-- JSON → Table
local new_tbl, pos, err = json.decode(json_str)
if err then error(err) end
print(new_tbl.data.value) -- 100
-- 自定义日期编码器(lua-cjson)
local cjson = require "cjson"
cjson.encode_function("mydate", function(dt)
return os.date("!%Y-%m-%dT%H:%M:%SZ", dt)
end)
local tbl = {timestamp=mydate(os.time())}
local json_str = cjson.encode(tbl) -- 输出包含 ISO8601 时间戳
-- 使用 Base64 编码
local base64 = require "base64"
local binary_data = file:read("*a")
local encoded = base64.encode(binary_data)
local decoded = base64.decode(encoded)
-- 预编译高频使用的结构
local template = cjson.new()
template.encode_sparse_array(false)
-- 复用预编译实例
local json_str = template:encode(tbl)
-- 复用 table(适用于频繁编解码场景)
local buffer = {}
for i = 1, 1e6 do
buffer:clear()
-- 填充数据到 buffer
cjson.encode(buffer)
end
-- 使用 LuaJIT FFI 直接操作内存
local ffi = require("ffi")
ffi.cdef[[ char* cjson_encode(lua_CFunction encoder, void* data); ]]
local json_c = ffi.load("lua-cjson")
local c_json = json_c.cjson_encode(encoder_ptr, data_ptr)
-- 编码时忽略敏感字段
local function filter_fields(tbl)
local copy = {}
for k, v in pairs(tbl) do
if k ~= "password" then
copy[k] = v
end
end
return copy
end
local safe_tbl = filter_fields(user_data)
local json_str = cjson.encode(safe_tbl)
-- 自定义编码钩子(lua-cjson)
cjson.encode_hook(function(t)
if t.__type == "uuid" then
return string.lower(t.value)
end
end)
-- 使用弱引用表避免循环引用
local weak_table = setmetatable({}, { __mode = "v" })
weak_table[1] = { name = "A" }
weak_table[2] = { name = "B", parent = weak_table[1] }
local function safe_encode(obj)
local cache = {}
return cjson.encode(obj, function(k, v)
if type(v) == "table" then
if cache[v] then
return cache[v] -- 返回已序列化的标识
end
cache[v] = "ref_" .. tostring(v)
end
return v
end)
end
-- 流式编码(分块写入)
local function stream_encode(file, tbl)
local encoder = cjson.new()
local generator = encoder.generator(tbl, { chunk_size = 4096 })
while true do
local chunk = generator()
if not chunk then break end
file:write(chunk)
end
end
场景 | lua-cjson (ops/s) | dkjson (ops/s) |
---|---|---|
编码 10KB table | 45,000 | 1,200 |
解码 10KB JSON | 62,000 | 850 |
编码 1MB table | 38,000 | 90 |
lua-cjson
:除非必须纯 Lua 实现collectgarbage()
控制内存local ok, result = pcall(cjson.decode, json_str)
if not ok then
logger:error("JSON解析失败: %s", result)
end
通过合理选择库和优化策略,可以实现 Lua 中高效可靠的 JSON 与 Table 转换。对于极端性能要求场景,建议结合 C 扩展或 LuaJIT FFI 实现。