JSON: Add encode / decode shortcut
[project/luci.git] / libs / json / luasrc / json.lua
index 3d6a576..5b09285 100644 (file)
@@ -52,24 +52,56 @@ local type      = type
 local pairs        = pairs
 local ipairs    = ipairs
 local next      = next
+local pcall            = pcall
 
 local getmetatable = getmetatable
 
+--- LuCI JSON-Library
+-- @cstyle     instance
 module "luci.json"
 
+
+--- Directly decode a JSON string
+-- @param json JSON-String
+-- @return Lua object
+function decode(json, ...)
+       local a = ActiveDecoder(function() return nil end, ...)
+       a.chunk = json
+       local s, obj = pcall(a.get, a)
+       return s and obj or nil
+end
+
+
+--- Direcly encode a Lua object into a JSON string.
+-- @param obj Lua Object
+-- @return JSON string
+function encode(obj, ...)
+       local out = {}
+       local e = Encoder(obj, 1, ...):source()
+       local chnk, err
+       repeat
+               chnk, err = e()
+               out[#out+1] = chnk
+       until chnk
+       return not err and table.concat(out) or nil
+end
+
+
 --- Null replacement function
 -- @return null
 function null()
        return null
 end
 
-
+--- Create a new JSON-Encoder.
+-- @class      function
+-- @name       Encoder
+-- @param data                 Lua-Object to be encoded.
+-- @param buffersize   Blocksize of returned data source.
+-- @param fastescape   Use non-standard escaping (don't escape control chars) 
+-- @return JSON-Encoder
 Encoder = util.class()
 
---- Creates a new Encoder.
--- @param data                 Data to be encoded.
--- @param buffersize   Buffersize of returned data.
--- @param fastescape   Use non-standard escaping (don't escape control chars) 
 function Encoder.__init__(self, data, buffersize, fastescape)
        self.data = data
        self.buffersize = buffersize or 512
@@ -79,7 +111,7 @@ function Encoder.__init__(self, data, buffersize, fastescape)
        getmetatable(self).__call = Encoder.source
 end
 
---- Create an LTN12 source from the encoder object
+--- Create an LTN12 source providing the encoded JSON-Data.
 -- @return LTN12 source
 function Encoder.source(self)
        local source = coroutine.create(self.dispatch)
@@ -180,10 +212,10 @@ function Encoder.parse_iter(self, obj)
                local first = true
                
                if type(obj) == "table" then
-                       for i, entry in pairs(obj) do
+                       for i=1, #obj do
                                first = first or self:put(",")
                                first = first and nil
-                               self:dispatch(entry)
+                               self:dispatch(obj[i])
                        end
                else
                        for entry in obj do
@@ -207,17 +239,19 @@ Encoder.parsers = {
 } 
 
 
-
+--- Create a new JSON-Decoder.
+-- @class      function
+-- @name       Decoder
+-- @param customnull Use luci.json.null instead of nil for decoding null
+-- @return JSON-Decoder
 Decoder = util.class()
 
---- Create a new Decoder object.
--- @param customnull User luci.json.null instead of nil
 function Decoder.__init__(self, customnull)
        self.cnull = customnull
        getmetatable(self).__call = Decoder.sink
 end
 
---- Create an LTN12 sink from the decoder object.
+--- Create an LTN12 sink from the decoder object which accepts the JSON-Data.
 -- @return LTN12 sink
 function Decoder.sink(self)
        local sink = coroutine.create(self.dispatch)
@@ -227,7 +261,7 @@ function Decoder.sink(self)
 end
 
 
---- Get the decoded data packets
+--- Get the decoded data packets after the rawdata has been sent to the sink.
 -- @return Decoded data
 function Decoder.get(self)
        return self.data
@@ -492,4 +526,41 @@ Decoder.parsers = {
        ['n'] = Decoder.parse_null,
        ['['] = Decoder.parse_array,
        ['{'] = Decoder.parse_object
-}
\ No newline at end of file
+}
+
+
+--- Create a new Active JSON-Decoder.
+-- @class      function
+-- @name       ActiveDecoder
+-- @param   customnull Use luci.json.null instead of nil for decoding null
+-- @return  Active JSON-Decoder
+ActiveDecoder = util.class(Decoder)
+
+function ActiveDecoder.__init__(self, source, customnull)
+       Decoder.__init__(self, customnull)
+       self.source = source
+       self.chunk = nil
+       getmetatable(self).__call = self.get
+end
+
+
+--- Fetches one JSON-object from given source
+-- @return Decoded object
+function ActiveDecoder.get(self)
+       local chunk, src_err, object
+       if not self.chunk then
+               chunk, src_err = self.source()
+       else
+               chunk = self.chunk
+       end
+
+       self.chunk, object = self:dispatch(chunk, src_err, true)
+       return object
+end
+
+
+function ActiveDecoder.fetch(self)
+       local chunk, src_err = self.source()
+       assert(chunk or not src_err, src_err)
+       return chunk
+end