docs/modules/sjson.md
| Since | Origin / Contributor | Maintainer | Source |
|---|---|---|---|
| 2017-02-01 | Philip Gladstone | Philip Gladstone | sjson |
The JSON support module. Allows encoding and decoding to/from JSON.
Please note that nested tables can require a lot of memory to encode. To catch out-of-memory errors, use pcall().
This code using the streaming json library jsonsl to do the parsing of the string.
This module can be used in two ways. The simpler way is to use it as a direct drop-in for cjson (you can just do _G.cjson = sjson).
The more advanced approach is to use the streaming interface. This allows encoding and decoding of significantly larger objects.
The handling of json null is as follows:
"\0".When encoding a Lua object, if a function is found, then it is invoked (with no arguments) and the (single) returned value is encoded in the place of the function.
!!! note
All examples below use in-memory JSON or content read from the SPIFFS file system. However, where a streaming implementation really shines is in fetching large JSON structures from the remote resources and extracting values on-the-fly. An elaborate streaming example can be found in the [`/lua_examples`](../../lua_examples/sjson-streaming.lua) folder.
This creates an encoder object that can convert a Lua object into a JSON encoded string.
####Syntax
sjson.encoder(table [, opts])
####Parameters
table data to encodeopts an optional table of options. The possible entries are:
depth the maximum encoding depth needed to encode the table. The default is 20 which should be enough for nearly all situations.null the string value to treat as null.####Returns
A sjson.encoder object.
This gets a chunk of JSON encoded data.
####Syntax
encoder:read([size])
####Parameters
size an optional value for the number of bytes to return. The default is 1024.####Returns
A string of up to size bytes, or nil if the encoding is complete and all data has been returned.
The following example prints out (in 64 byte chunks) a JSON encoded string containing the first 4k of every file in the file system. The total string can be bigger than the total amount of memory on the NodeMCU.
function files()
result = {}
for k,v in pairs(file.list()) do
result[k] = function() return file.open(k):read(4096) end
end
return result
end
local encoder = sjson.encoder(files())
while true do
data = encoder:read(64)
if not data then
break
end
print(data)
end
Encode a Lua table to a JSON string. This is a convenience method provided for backwards compatibility with cjson.
####Syntax
sjson.encode(table [, opts])
####Parameters
table data to encodeopts an optional table of options. The possible entries are:
depth the maximum encoding depth needed to encode the table. The default is 20 which should be enough for nearly all situations.null the string value to treat as null.####Returns JSON string
####Example
ok, json = pcall(sjson.encode, {key="value"})
if ok then
print(json)
else
print("failed to encode!")
end
This makes a decoder object that can parse a JSON encoded string into a Lua object. A metatable can be specified for all the newly created Lua tables. This allows
you to handle each value as it is inserted into each table (by implementing the __newindex method).
####Syntax
sjson.decoder([opts])
opts an optional table of options. The possible entries are:
depth the maximum encoding depth needed to encode the table. The default is 20 which should be enough for nearly all situations.null the string value to treat as null.metatable a table to use as the metatable for all the new tables in the returned object.A sjson.decoder object
####Metatable
There are two principal methods that are invoked in the metatable (if it is present).
__newindex this is the standard method invoked whenever a new table element is created.checkpath this is invoked (if defined) whenever a new table is created. It is invoked with two arguments:
table this is the newly created tablepath this is a list of the keys from the root.
It must return true if this object is wanted in the result, or false otherwise.For example, when decoding { "foo": [1, 2, []] } the checkpath will be invoked as follows:
checkpath({}, {}) the table argument is the object that will correspond with the value of the JSON object.checkpath({}, {"foo"}) the table argument is the object that will correspond with the value of the outer JSON array.checkpath({}, {"foo", 3}) the table argument is the object that will correspond to the empty inner JSON array.When the checkpath method is called, the metatable has already be associated with the new table. Thus the checkpath method can replace it
if desired. For example, if you are decoding { "foo": { "bar": [1,2,3,4], "cat": [5] } } and, for some reason, you did not want to capture the
value of the "bar" key, then there are various ways to do this:
In the __newindex metamethod, just check for the value of the key and skip the rawset if the key is "bar". This only works if you want to skip all the
"bar" keys.
In the checkpath method, if the path is ["foo"], then return false.
Use the following checkpath: checkpath=function(tab, path) tab['__json_path'] = path return true end This will save the path in each constructed object. Now the __newindex method can perform more sophisticated filtering.
The reason for being able to filter is that it enables processing of very large JSON responses on a memory constrained platform. Many APIs return lots of information
which would exceed the memory budget of the platform. For example, https://api.github.com/repos/nodemcu/nodemcu-firmware/contents is over 13kB, and yet, if
you only need the download_url keys, then the total size is around 600B. This can be handled with a simple __newindex method.
This provides more data to be parsed into the Lua object.
####Syntax
decoder:write(string)
####Parameters
string the next piece of JSON encoded data####Returns
The constructed Lua object or nil if the decode is not yet complete.
####Errors If a parse error occurrs during this decode, then an error is thrown and the parse is aborted. The object cannot be used again.
This gets the decoded Lua object, or raises an error if the decode is not yet complete. This can be called multiple times and will return the same object each time.
####Syntax
decoder:result()
####Errors If the decode is not complete, then an error is thrown.
####Example
local decoder = sjson.decoder()
decoder:write("[10, 1")
decoder:write("1")
decoder:write(", \"foo\"]")
for k,v in pairs(decoder:result()) do
print (k, v)
end
The next example demonstrates the use of the metatable argument. In this case it just prints out the operations, but it could suppress the assignment altogether if desired.
local decoder = sjson.decoder({metatable=
{__newindex=function(t,k,v) print("Setting '" .. k .. "' = '" .. tostring(v) .."'")
rawset(t,k,v) end}})
decoder:write('[1, 2, {"foo":"bar"}]')
Decode a JSON string to a Lua table. This is a convenience method provided for backwards compatibility with cjson.
####Syntax
sjson.decode(str[, opts])
####Parameters
str JSON string to decodeopts an optional table of options. The possible entries are:
depth the maximum encoding depth needed to encode the table. The default is 20 which should be enough for nearly all situations.null the string value to treat as null.metatable a table to use as the metatable for all the new tables in the returned object. See the metatable section in the description of sjson.decoder() above.####Returns Lua table representation of the JSON data
####Errors If the string is not valid JSON, then an error is thrown.
####Example
t = sjson.decode('{"key":"value"}')
for k,v in pairs(t) do print(k,v) end
##Constants
There is one constant, sjson.NULL, which is used in Lua structures to represent the presence of a JSON null.