@@ -47,6 +47,40 @@ local function get_validator(schema)
4747 return nil
4848end
4949
50+ --- Collect all possible types from a schema, including composite sub-schemas.
51+ local function collect_types (schema , seen )
52+ if not schema then return {} end
53+ seen = seen or {}
54+ if seen [schema ] then return {} end
55+ seen [schema ] = true
56+
57+ local types = {}
58+ local stype = schema .type
59+ if stype then
60+ if type (stype ) == " table" then
61+ for _ , t in ipairs (stype ) do
62+ types [t ] = true
63+ end
64+ else
65+ types [stype ] = true
66+ end
67+ end
68+
69+ for _ , key in ipairs ({" anyOf" , " oneOf" , " allOf" }) do
70+ local composite = schema [key ]
71+ if composite then
72+ for _ , sub in ipairs (composite ) do
73+ local sub_types = collect_types (sub , seen )
74+ for t in pairs (sub_types ) do
75+ types [t ] = true
76+ end
77+ end
78+ end
79+ end
80+
81+ return types
82+ end
83+
5084--- Coerce a string value to the type declared in schema.
5185-- @param value string raw value from request
5286-- @param schema table parameter schema
@@ -87,6 +121,20 @@ local function coerce_value(value, schema)
87121 return value
88122 end
89123
124+ -- no direct type — check composite schemas for possible types
125+ if not stype then
126+ local possible = collect_types (schema )
127+ -- try coercion in order: boolean first (most specific), then number
128+ if possible [" boolean" ] then
129+ if value == " true" or value == " 1" then return true end
130+ if value == " false" or value == " 0" then return false end
131+ end
132+ if possible [" integer" ] or possible [" number" ] then
133+ local n = tonumber (value )
134+ if n then return n end
135+ end
136+ end
137+
90138 return value
91139end
92140
187235-- query: form, explode=true
188236-- header: simple, explode=false
189237local function deserialize_param (raw_value , param , query_args )
238+ -- handle content-based parameters (param.content.application/json) first,
239+ -- since these params have no param.schema (schema lives inside content)
240+ if param .content then
241+ local json_content = param .content [" application/json" ]
242+ if json_content and json_content .schema and has_cjson then
243+ local decoded = cjson .decode (raw_value )
244+ if decoded ~= nil then
245+ return decoded
246+ end
247+ end
248+ end
249+
190250 local schema = param .schema
191251 if not schema then
192252 return raw_value
@@ -208,17 +268,6 @@ local function deserialize_param(raw_value, param, query_args)
208268 explode = (style == " form" )
209269 end
210270
211- -- handle content-based parameters (param.content.application/json)
212- if param .content then
213- local json_content = param .content [" application/json" ]
214- if json_content and json_content .schema and has_cjson then
215- local decoded = cjson .decode (raw_value )
216- if decoded ~= nil then
217- return decoded
218- end
219- end
220- end
221-
222271 local stype = schema .type
223272
224273 if stype == " array" then
@@ -351,13 +400,25 @@ function _M.validate(route, path_params, query_args, headers, skip)
351400 end
352401
353402 local schema = param .schema
403+ -- for content-based parameters, use the content schema
404+ if not schema and param .content then
405+ local json_ct = param .content [" application/json" ]
406+ if json_ct then
407+ schema = json_ct .schema
408+ end
409+ end
354410 if not schema then
355411 goto continue
356412 end
357413
358414 -- deserialize and coerce
359415 local value = deserialize_param (raw , param , query_args )
360416
417+ -- for deepObject, nil means no matching keys found — skip if optional
418+ if value == nil and not param .required then
419+ goto continue
420+ end
421+
361422 -- validate with jsonschema
362423 local validator = get_validator (schema )
363424 if validator then
0 commit comments