all: 5.2 compatibility
[luajson.git] / lua / json / encode.lua
blobddb7c82ebfd2e7f9b8cd1163a1d6511ef4e65278
1 --[[
2 Licensed according to the included 'LICENSE' document
3 Author: Thomas Harning Jr <harningt@gmail.com>
4 ]]
5 local type = type
6 local assert, error = assert, error
7 local getmetatable, setmetatable = getmetatable, setmetatable
8 local util = require("json.util")
10 local ipairs, pairs = ipairs, pairs
11 local require = require
13 local output = require("json.encode.output")
15 local util = require("json.util")
16 local util_merge, isCall = util.merge, util.isCall
18 local is_52 = _VERSION == "Lua 5.2"
19 local _G = _G
21 if is_52 then
22 _ENV = nil
23 end
25 --[[
26 List of encoding modules to load.
27 Loaded in sequence such that earlier encoders get priority when
28 duplicate type-handlers exist.
30 local modulesToLoad = {
31 "strings",
32 "number",
33 "calls",
34 "others",
35 "array",
36 "object"
38 -- Modules that have been loaded
39 local loadedModules = {}
41 -- Default configuration options to apply
42 local defaultOptions = {}
43 -- Configuration bases for client apps
44 local default = nil
45 local strict = {
46 initialObject = true -- Require an object at the root
49 -- For each module, load it and its defaults
50 for _,name in ipairs(modulesToLoad) do
51 local mod = require("json.encode." .. name)
52 defaultOptions[name] = mod.default
53 strict[name] = mod.strict
54 loadedModules[name] = mod
55 end
57 -- Merges values, assumes all tables are arrays, inner values flattened, optionally constructing output
58 local function flattenOutput(out, values)
59 out = not out and {} or type(out) == 'table' and out or {out}
60 if type(values) == 'table' then
61 for _, v in ipairs(values) do
62 out[#out + 1] = v
63 end
64 else
65 out[#out + 1] = values
66 end
67 return out
68 end
70 -- Prepares the encoding map from the already provided modules and new config
71 local function prepareEncodeMap(options)
72 local map = {}
73 for _, name in ipairs(modulesToLoad) do
74 local encodermap = loadedModules[name].getEncoder(options[name])
75 for valueType, encoderSet in pairs(encodermap) do
76 map[valueType] = flattenOutput(map[valueType], encoderSet)
77 end
78 end
79 return map
80 end
82 --[[
83 Encode a value with a given encoding map and state
85 local function encodeWithMap(value, map, state, isObjectKey)
86 local t = type(value)
87 local encoderList = assert(map[t], "Failed to encode value, unhandled type: " .. t)
88 for _, encoder in ipairs(encoderList) do
89 local ret = encoder(value, state, isObjectKey)
90 if false ~= ret then
91 return ret
92 end
93 end
94 error("Failed to encode value, encoders for " .. t .. " deny encoding")
95 end
98 local function getBaseEncoder(options)
99 local encoderMap = prepareEncodeMap(options)
100 if options.preProcess then
101 local preProcess = options.preProcess
102 return function(value, state, isObjectKey)
103 local ret = preProcess(value, isObjectKey or false)
104 if nil ~= ret then
105 value = ret
107 return encodeWithMap(value, encoderMap, state)
110 return function(value, state, isObjectKey)
111 return encodeWithMap(value, encoderMap, state)
114 --[[
115 Retreive an initial encoder instance based on provided options
116 the initial encoder is responsible for initializing state
117 State has at least these values configured: encode, check_unique, already_encoded
119 local function getEncoder(options)
120 options = options and util_merge({}, defaultOptions, options) or defaultOptions
121 local encode = getBaseEncoder(options)
123 local function initialEncode(value)
124 if options.initialObject then
125 local errorMessage = "Invalid arguments: expects a JSON Object or Array at the root"
126 assert(type(value) == 'table' and not isCall(value, options), errorMessage)
129 local alreadyEncoded = {}
130 local function check_unique(value)
131 assert(not alreadyEncoded[value], "Recursive encoding of value")
132 alreadyEncoded[value] = true
135 local outputEncoder = options.output and options.output() or output.getDefault()
136 local state = {
137 encode = encode,
138 check_unique = check_unique,
139 already_encoded = alreadyEncoded, -- To unmark encoding when moving up stack
140 outputEncoder = outputEncoder
142 local ret = encode(value, state)
143 if nil ~= ret then
144 return outputEncoder.simple and outputEncoder.simple(ret) or ret
147 return initialEncode
150 -- CONSTRUCT STATE WITH FOLLOWING (at least)
151 --[[
152 encoder
153 check_unique -- used by inner encoders to make sure value is unique
154 already_encoded -- used to unmark a value as unique
156 local function encode(data, options)
157 return getEncoder(options)(data)
160 local mt = {}
161 mt.__call = function(self, ...)
162 return encode(...)
165 local json_encode = {
166 default = default,
167 strict = strict,
168 getEncoder = getEncoder,
169 encode = encode
171 setmetatable(json_encode, mt)
173 if not is_52 then
174 _G.json = _G.json or {}
175 _G.json.encode = util_merge(json_encode, _G.json.encode)
177 return json_encode