diff --git a/docs/en/modules/sjson.md b/docs/en/modules/sjson.md index 1bf3ed7b..03da3aff 100644 --- a/docs/en/modules/sjson.md +++ b/docs/en/modules/sjson.md @@ -21,6 +21,10 @@ anywhere else in your data structures. A suitable value might be `"\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. + ## sjson.encoder() This creates an encoder object that can convert a Lua object into a JSON encoded string. @@ -53,7 +57,8 @@ A string of up to `size` bytes, or `nil` if the encoding is complete and all dat #### Example 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. -``` + +```lua function files() result = {} for k,v in pairs(file.list()) do @@ -230,5 +235,5 @@ 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. +There is one constant, `sjson.NULL`, which is used in Lua structures to represent the presence of a JSON null. diff --git a/lua_examples/sjson-streaming.lua b/lua_examples/sjson-streaming.lua new file mode 100644 index 00000000..d7981c5f --- /dev/null +++ b/lua_examples/sjson-streaming.lua @@ -0,0 +1,70 @@ +-- Test sjson and GitHub API + +local s = tls.createConnection() +s:on("connection", function(sck, c) + sck:send("GET /repos/nodemcu/nodemcu-firmware/git/trees/master HTTP/1.0\r\nUser-agent: nodemcu/0.1\r\nHost: api.github.com\r\nConnection: close\r\nAccept: application/json\r\n\r\n") +end) + +function startswith(String, Start) + return string.sub(String, 1, string.len(Start)) == Start +end + +local seenBlank = false +local partial +local wantval = { tree = 1, path = 1, url = 1 } +-- Make an sjson decoder that only keeps certain fields +local decoder = sjson.decoder({ + metatable = + { + __newindex = function(t, k, v) + if wantval[k] or type(k) == "number" then + rawset(t, k, v) + end + end + } +}) +local function handledata(s) + decoder:write(s) +end + +-- The receive callback is somewhat gnarly as it has to deal with find the end of the header +-- and having the newline sequence split across packets +s:on("receive", function(sck, c) + if partial then + c = partial .. c + partial = nil + end + if seenBlank then + handledata(c) + return + end + while c do + if startswith(c, "\r\n") then + seenBlank = true + c = c:sub(3) + handledata(c) + return + end + local s, e = c:find("\r\n") + if s then + -- Throw away line + c = c:sub(e + 1) + else + partial = c + c = nil + end + end +end) + +local function getresult() + local result = decoder:result() + -- This gets the resulting decoded object with only the required fields + print(result['tree'][4]['path'], "is at", + result['tree'][4]['url']) +end + +s:on("disconnection", getresult) +s:on("reconnection", getresult) + +-- Make it all happen! +s:connect(443, "api.github.com")