2 Licensed according to the included 'LICENSE' document
3 Author: Thomas Harning Jr <harningt@gmail.com>
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"
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
= {
38 -- Modules that have been loaded
39 local loadedModules
= {}
41 -- Default configuration options to apply
42 local defaultOptions
= {}
43 -- Configuration bases for client apps
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
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
65 out
[#out
+ 1] = values
70 -- Prepares the encoding map from the already provided modules and new config
71 local function prepareEncodeMap(options
)
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
)
83 Encode a value with a given encoding map and state
85 local function encodeWithMap(value
, map
, state
, isObjectKey
)
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
)
94 error("Failed to encode value, encoders for " .. t
.. " deny encoding")
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)
107 return encodeWithMap(value
, encoderMap
, state
)
110 return function(value
, state
, isObjectKey
)
111 return encodeWithMap(value
, encoderMap
, state
)
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()
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
)
144 return outputEncoder
.simple
and outputEncoder
.simple(ret
) or ret
150 -- CONSTRUCT STATE WITH FOLLOWING (at least)
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
)
161 mt
.__call
= function(self
, ...)
165 local json_encode
= {
168 getEncoder
= getEncoder
,
171 setmetatable(json_encode
, mt
)
174 _G
.json
= _G
.json
or {}
175 _G
.json
.encode
= util_merge(json_encode
, _G
.json
.encode
)