JSON for Lua
Just copy "json.lua" to Lua modules' folder.
local json = require('json')
print(json.encode({ 1, 2, 'fred', {first='mars',second='venus',third='earth'} }))
[1,2,"fred",{"first":"mars","second":"venus","third":"earth"}]
local json = require("json")
local testString = [[ { "one":1 , "two":2, "primes":[2,3,5,7] } ]]
local decoded = json.decode(testString)
for k, v in pairs(decoded) do
print('', k, v)
end
print("Primes are:")
for k, v in ipairs(decoded.primes) do
print('', k, v)
end
one 1
two 2
primes table: 0x8454688
Primes are:
1 2
2 3
3 5
4 7
Traverse is useful to reduce memory usage: no memory-consuming objects are being created in Lua while traversing.
Function json.traverse(s, callback, pos)
traverses JSON and invokes user-supplied callback function for each item found inside JSON.
callback
function will be invoked with the following arguments:
callback(path, json_type, value, pos, pos_last)
path is array of nested JSON identifiers, this array is empty for root JSON element
json_type is one of "null"/"boolean"/"number"/"string"/"array"/"object"
value is element's value for "null"/"boolean"/"number"/"string", nil for "object"/"array"
pos is 1-based index of first character of current JSON element
pos_last is 1-based index of last character of current JSON element (defined only when value ~= nil)
By default, value
is nil
for JSON arrays/objects.
Nevertheless, you can get any array/object decoded (instead of get traversed) while traversing JSON.
In order to do that, callback
function must return true
when invoked for that element (when value == nil
).
This array/object (decoded as Lua value) will be sent to you on next invocation of callback function (value ~= nil
).
Traverse example:
local json = require("json")
local JSON_string =
[[ {"a":true, "b":null, "c":["one","two"], "d":{ "e":{}, "f":[] }, "g":["ten",20,-33.3] } ]]
json.traverse(JSON_string, callback)
-- callback function will be invoked 13 times (if it returns true for array "c" and object "e"):
-- path json_type value pos pos_last
-- ---------- --------- -------------- --- --------
-- callback( {}, "object", nil, 2, nil )
-- callback( {"a"}, "boolean", true, 7, 10 )
-- callback( {"b"}, "null", json.null, 17, 20 ) -- special Lua value for JSON null
-- callback( {"c"}, "array", nil, 27, nil ) -- this callback returned true (user wants to decode this array)
-- callback( {"c"}, "array", {"one", "two"}, 27, 39 ) -- the next invocation brings the result of decoding (value ~= nil)
-- callback( {"d"}, "object", nil, 46, nil )
-- callback( {"d", "e"}, "object", nil, 52, nil ) -- this callback returned true (user wants to decode this object)
-- callback( {"d", "e"}, "object", json.empty, 52, 53 ) -- the next invocation brings the result of decoding (special Lua value for empty JSON object)
-- callback( {"d", "f"}, "array", nil, 60, nil )
-- callback( {"g"}, "array", nil, 70, nil )
-- callback( {"g", 1}, "string", "ten", 71, 75 )
-- callback( {"g", 2}, "number", 20, 77, 78 )
-- callback( {"g", 3}, "number", -33.3, 80, 84 )
Example of callback function to get c
and e
elements been decoded (instead of traversed):
local result_c, result_e -- these variables will hold Lua objects for JSON elements "c" and "e"
local function callback(path, json_type, value, pos, pos_last)
local elem_path = table.concat(path, '/') -- element's path
-- print(elem_path, json_type, value, pos, pos_last)
if elem_path == 'c' then
result_c = value
elseif elem_path == 'd/e' then
result_e = value
end
return elem_path == 'c' or elem_path == 'd/e' -- we want "c" and "e" to be decoded instead of be traversed
end
json.traverse(JSON_string, callback)
-- Now variables "result_c" and "result_e" contain decoded JSON elements "c" and "e"
-- result_c == {"one", "two"}
-- result_e == json.empty
Both functions json.decode()
and json.traverse()
can accept JSON as a "loader function" instead of a "whole JSON string".
This function will be called repeatedly to return next parts (substrings) of JSON.
An empty string, nil
, or no value returned from "loader function" means the end of JSON.
This may be useful for low-memory devices or for traversing huge JSON files.
-- Open file
local file = assert(io.open('large_json.txt', 'r'))
-- Define loader function for reading the file in 4KByte chunks
-- (64 Byte chunks are recommended for RAM-restricted devices)
local function my_json_loader()
return file:read(4*1024)
end
if you_want_to_traverse_JSON_or_to_decode_JSON_partially then
-- Prepare callback function
local function my_callback (path, json_type, value, pos, last_pos)
-- Do whatever you need here
-- (see examples on using json.traverse)
end
-- Do traverse
-- Set initial position as 3-rd argument (default 1) if JSON is stored not from the beginning of your file
json.traverse(my_json_loader, my_callback)
elseif you_want_to_decode_the_whole_JSON then
-- Do decode
-- Set initial position as 2-nd argument (default 1) if JSON is stored not from the beginning of your file
result = json.decode(my_json_loader)
end
-- Close file
file:close()