license: updates copyright year
[luajson.git] / lua / json / encode.lua
blob5a13adc330773dd14758e4bcaa79c3d201d0be00
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
9 local ipairs, pairs = ipairs, pairs
10 local require = require
12 local output = require("json.encode.output")
14 local util = require("json.util")
15 local util_merge, isCall = util.merge, util.isCall
17 local _ENV = nil
19 --[[
20 List of encoding modules to load.
21 Loaded in sequence such that earlier encoders get priority when
22 duplicate type-handlers exist.
24 local modulesToLoad = {
25 "strings",
26 "number",
27 "calls",
28 "others",
29 "array",
30 "object"
32 -- Modules that have been loaded
33 local loadedModules = {}
35 local json_encode = {}
37 -- Configuration bases for client apps
38 local modes_defined = { "default", "strict" }
40 json_encode.default = {}
41 json_encode.strict = {
42 initialObject = true -- Require an object at the root
45 -- For each module, load it and its defaults
46 for _,name in ipairs(modulesToLoad) do
47 local mod = require("json.encode." .. name)
48 if mod.mergeOptions then
49 for _, mode in pairs(modes_defined) do
50 mod.mergeOptions(json_encode[mode], mode)
51 end
52 end
53 loadedModules[name] = mod
54 end
56 -- NOTE: Nested not found, so assume unsupported until use case arises
57 local function flattenOutput(out, value)
58 assert(type(value) ~= 'table')
59 out = out or {}
60 out[#out + 1] = value
61 return out
62 end
64 -- Prepares the encoding map from the already provided modules and new config
65 local function prepareEncodeMap(options)
66 local map = {}
67 for _, name in ipairs(modulesToLoad) do
68 local encodermap = loadedModules[name].getEncoder(options[name])
69 for valueType, encoderSet in pairs(encodermap) do
70 map[valueType] = flattenOutput(map[valueType], encoderSet)
71 end
72 end
73 return map
74 end
76 --[[
77 Encode a value with a given encoding map and state
79 local function encodeWithMap(value, map, state, isObjectKey)
80 local t = type(value)
81 local encoderList = assert(map[t], "Failed to encode value, unhandled type: " .. t)
82 for _, encoder in ipairs(encoderList) do
83 local ret = encoder(value, state, isObjectKey)
84 if false ~= ret then
85 return ret
86 end
87 end
88 error("Failed to encode value, encoders for " .. t .. " deny encoding")
89 end
92 local function getBaseEncoder(options)
93 local encoderMap = prepareEncodeMap(options)
94 if options.preProcess then
95 local preProcess = options.preProcess
96 return function(value, state, isObjectKey)
97 local ret = preProcess(value, isObjectKey or false)
98 if nil ~= ret then
99 value = ret
101 return encodeWithMap(value, encoderMap, state)
104 return function(value, state, isObjectKey)
105 return encodeWithMap(value, encoderMap, state)
108 --[[
109 Retreive an initial encoder instance based on provided options
110 the initial encoder is responsible for initializing state
111 State has at least these values configured: encode, check_unique, already_encoded
113 function json_encode.getEncoder(options)
114 options = options and util_merge({}, json_encode.default, options) or json_encode.default
115 local encode = getBaseEncoder(options)
117 local function initialEncode(value)
118 if options.initialObject then
119 local errorMessage = "Invalid arguments: expects a JSON Object or Array at the root"
120 assert(type(value) == 'table' and not isCall(value, options), errorMessage)
123 local alreadyEncoded = {}
124 local function check_unique(value)
125 assert(not alreadyEncoded[value], "Recursive encoding of value")
126 alreadyEncoded[value] = true
129 local outputEncoder = options.output and options.output() or output.getDefault()
130 local state = {
131 encode = encode,
132 check_unique = check_unique,
133 already_encoded = alreadyEncoded, -- To unmark encoding when moving up stack
134 outputEncoder = outputEncoder
136 local ret = encode(value, state)
137 if nil ~= ret then
138 return outputEncoder.simple and outputEncoder.simple(ret) or ret
141 return initialEncode
144 -- CONSTRUCT STATE WITH FOLLOWING (at least)
145 --[[
146 encoder
147 check_unique -- used by inner encoders to make sure value is unique
148 already_encoded -- used to unmark a value as unique
150 function json_encode.encode(data, options)
151 return json_encode.getEncoder(options)(data)
154 local mt = {}
155 mt.__call = function(self, ...)
156 return json_encode.encode(...)
159 setmetatable(json_encode, mt)
161 return json_encode