Software /
code /
prosody
Comparison
teal-src/util/jsonschema.tl @ 12132:4ff0d33dfb2b
util.jsonschema: Add support for $ref pointers
author | Kim Alvefur <zash@zash.se> |
---|---|
date | Wed, 29 Dec 2021 16:57:35 +0100 |
parent | 11460:a8b4e04bc044 |
child | 12579:ca6a43fe0231 |
comparison
equal
deleted
inserted
replaced
12131:b4c0efff8dd3 | 12132:4ff0d33dfb2b |
---|---|
9 -- | 9 -- |
10 | 10 |
11 local json = require"util.json" | 11 local json = require"util.json" |
12 local null = json.null; | 12 local null = json.null; |
13 | 13 |
14 local pointer = require "util.jsonpointer" | |
15 | |
14 local type json_type_name = json.json_type_name | 16 local type json_type_name = json.json_type_name |
15 | 17 |
16 -- json_type_name here is non-standard | 18 -- json_type_name here is non-standard |
17 local type schema_t = boolean | json_type_name | json_schema_object | 19 local type schema_t = boolean | json_type_name | json_schema_object |
18 | 20 |
30 | 32 |
31 ["not"] : schema_t | 33 ["not"] : schema_t |
32 ["if"] : schema_t | 34 ["if"] : schema_t |
33 ["then"] : schema_t | 35 ["then"] : schema_t |
34 ["else"] : schema_t | 36 ["else"] : schema_t |
37 | |
38 ["$ref"] : string | |
35 | 39 |
36 -- numbers | 40 -- numbers |
37 multipleOf : number | 41 multipleOf : number |
38 maximum : number | 42 maximum : number |
39 exclusiveMaximum : number | 43 exclusiveMaximum : number |
88 deprecated : boolean | 92 deprecated : boolean |
89 readOnly : boolean | 93 readOnly : boolean |
90 writeOnly : boolean | 94 writeOnly : boolean |
91 | 95 |
92 -- methods | 96 -- methods |
93 validate : function ( schema_t, any) : boolean | 97 validate : function ( schema_t, any, json_schema_object ) : boolean |
94 end | 98 end |
95 | 99 |
96 -- TODO validator function per schema property | 100 -- TODO validator function per schema property |
97 | 101 |
98 local type_validators : { json_type_name : function (schema_t, any) : boolean } = {} | 102 local type_validators : { json_type_name : function (schema_t, any, json_schema_object) : boolean } = {} |
99 | 103 |
100 local function simple_validate(schema : json_type_name, data : any) : boolean | 104 local function simple_validate(schema : json_type_name, data : any) : boolean |
101 if schema == "object" and data is table then | 105 if schema == "object" and data is table then |
102 return type(data) == "table" and (next(data)==nil or type((next(data, nil))) == "string") | 106 return type(data) == "table" and (next(data)==nil or type((next(data, nil))) == "string") |
103 elseif schema == "array" and data is table then | 107 elseif schema == "array" and data is table then |
150 return true | 154 return true |
151 end | 155 end |
152 | 156 |
153 type_validators.integer = type_validators.number | 157 type_validators.integer = type_validators.number |
154 | 158 |
155 local function validate(schema : schema_t, data : any) : boolean | 159 local function validate(schema : schema_t, data : any, root : json_schema_object) : boolean |
156 if schema is boolean then | 160 if schema is boolean then |
157 return schema | 161 return schema |
158 end | 162 end |
159 if schema is json_type_name then | 163 if schema is json_type_name then |
160 return simple_validate(schema, data) | 164 return simple_validate(schema, data) |
161 end | 165 end |
162 if schema is json_schema_object then | 166 if schema is json_schema_object then |
167 if root == nil then | |
168 root = schema | |
169 end | |
170 if schema["$ref"] and schema["$ref"]:sub(1,1) == "#" then | |
171 local referenced = pointer.resolve(root as table, schema["$ref"]:sub(2)) as schema_t | |
172 if referenced ~= nil then | |
173 return validate(referenced, data, root); | |
174 end | |
175 end | |
176 | |
163 if schema.allOf then | 177 if schema.allOf then |
164 for _, sub in ipairs(schema.allOf) do | 178 for _, sub in ipairs(schema.allOf) do |
165 if not validate(sub, data) then | 179 if not validate(sub, data, root) then |
166 return false | 180 return false |
167 end | 181 end |
168 end | 182 end |
169 return true | 183 return true |
170 end | 184 end |
171 | 185 |
172 if schema.oneOf then | 186 if schema.oneOf then |
173 local valid = 0 | 187 local valid = 0 |
174 for _, sub in ipairs(schema.oneOf) do | 188 for _, sub in ipairs(schema.oneOf) do |
175 if validate(sub, data) then | 189 if validate(sub, data, root) then |
176 valid = valid + 1 | 190 valid = valid + 1 |
177 end | 191 end |
178 end | 192 end |
179 return valid == 1 | 193 return valid == 1 |
180 end | 194 end |
181 | 195 |
182 if schema.anyOf then | 196 if schema.anyOf then |
183 for _, sub in ipairs(schema.anyOf) do | 197 for _, sub in ipairs(schema.anyOf) do |
184 if validate(sub, data) then | 198 if validate(sub, data, root) then |
185 return true | 199 return true |
186 end | 200 end |
187 end | 201 end |
188 return false | 202 return false |
189 end | 203 end |
190 | 204 |
191 if schema["not"] then | 205 if schema["not"] then |
192 if validate(schema["not"], data) then | 206 if validate(schema["not"], data, root) then |
193 return false | 207 return false |
194 end | 208 end |
195 end | 209 end |
196 | 210 |
197 if schema["if"] then | 211 if schema["if"] then |
198 if validate(schema["if"], data) then | 212 if validate(schema["if"], data, root) then |
199 if schema["then"] then | 213 if schema["then"] then |
200 return validate(schema["then"], data) | 214 return validate(schema["then"], data, root) |
201 end | 215 end |
202 else | 216 else |
203 if schema["else"] then | 217 if schema["else"] then |
204 return validate(schema["else"], data) | 218 return validate(schema["else"], data, root) |
205 end | 219 end |
206 end | 220 end |
207 end | 221 end |
208 | 222 |
209 if schema.const ~= nil and schema.const ~= data then | 223 if schema.const ~= nil and schema.const ~= data then |
224 return false | 238 return false |
225 end | 239 end |
226 | 240 |
227 local validator = type_validators[schema.type] | 241 local validator = type_validators[schema.type] |
228 if validator then | 242 if validator then |
229 return validator(schema, data) | 243 return validator(schema, data, root) |
230 end | 244 end |
231 end | 245 end |
232 return true | 246 return true |
233 end | 247 end |
234 end | 248 end |
235 | 249 |
236 type_validators.table = function (schema : json_schema_object, data : any) : boolean | 250 type_validators.table = function (schema : json_schema_object, data : any, root : json_schema_object) : boolean |
237 if data is table then | 251 if data is table then |
238 | 252 |
239 if schema.maxItems and #data > schema.maxItems then | 253 if schema.maxItems and #data > schema.maxItems then |
240 return false | 254 return false |
241 end | 255 end |
253 end | 267 end |
254 | 268 |
255 if schema.properties then | 269 if schema.properties then |
256 local additional : schema_t = schema.additionalProperties or true | 270 local additional : schema_t = schema.additionalProperties or true |
257 for k, v in pairs(data) do | 271 for k, v in pairs(data) do |
258 if schema.propertyNames and not validate(schema.propertyNames, k) then | 272 if schema.propertyNames and not validate(schema.propertyNames, k, root) then |
259 return false | 273 return false |
260 end | 274 end |
261 local s = schema.properties[k as string] or additional | 275 local s = schema.properties[k as string] or additional |
262 if not validate(s, v) then | 276 if not validate(s, v, root) then |
263 return false | 277 return false |
264 end | 278 end |
265 end | 279 end |
266 elseif schema.additionalProperties then | 280 elseif schema.additionalProperties then |
267 for k, v in pairs(data) do | 281 for k, v in pairs(data) do |
268 if schema.propertyNames and not validate(schema.propertyNames, k) then | 282 if schema.propertyNames and not validate(schema.propertyNames, k, root) then |
269 return false | 283 return false |
270 end | 284 end |
271 if not validate(schema.additionalProperties, v) then | 285 if not validate(schema.additionalProperties, v, root) then |
272 return false | 286 return false |
273 end | 287 end |
274 end | 288 end |
275 end | 289 end |
276 | 290 |
286 end | 300 end |
287 | 301 |
288 local p = 0 | 302 local p = 0 |
289 if schema.prefixItems then | 303 if schema.prefixItems then |
290 for i, s in ipairs(schema.prefixItems) do | 304 for i, s in ipairs(schema.prefixItems) do |
291 if validate(s, data[i]) then | 305 if validate(s, data[i], root) then |
292 p = i | 306 p = i |
293 else | 307 else |
294 return false | 308 return false |
295 end | 309 end |
296 end | 310 end |
297 end | 311 end |
298 | 312 |
299 if schema.items then | 313 if schema.items then |
300 for i = p+1, #data do | 314 for i = p+1, #data do |
301 if not validate(schema.items, data[i]) then | 315 if not validate(schema.items, data[i], root) then |
302 return false | 316 return false |
303 end | 317 end |
304 end | 318 end |
305 end | 319 end |
306 | 320 |
307 if schema.contains then | 321 if schema.contains then |
308 local found = false | 322 local found = false |
309 for i = 1, #data do | 323 for i = 1, #data do |
310 if validate(schema.contains, data[i]) then | 324 if validate(schema.contains, data[i], root) then |
311 found = true | 325 found = true |
312 break | 326 break |
313 end | 327 end |
314 end | 328 end |
315 if not found then | 329 if not found then |
320 return true | 334 return true |
321 end | 335 end |
322 return false | 336 return false |
323 end | 337 end |
324 | 338 |
325 type_validators.object = function (schema : schema_t, data : any) : boolean | 339 type_validators.object = function (schema : schema_t, data : any, root : json_schema_object) : boolean |
326 if data is table then | 340 if data is table then |
327 for k in pairs(data) do | 341 for k in pairs(data) do |
328 if not k is string then | 342 if not k is string then |
329 return false | 343 return false |
330 end | 344 end |
331 end | 345 end |
332 | 346 |
333 return type_validators.table(schema, data) | 347 return type_validators.table(schema, data, root) |
334 end | 348 end |
335 return false | 349 return false |
336 end | 350 end |
337 | 351 |
338 type_validators.array = function (schema : schema_t, data : any) : boolean | 352 type_validators.array = function (schema : schema_t, data : any, root : json_schema_object) : boolean |
339 if data is table then | 353 if data is table then |
340 | 354 |
341 -- just check that there the keys are all numbers | 355 -- just check that there the keys are all numbers |
342 for i in pairs(data) do | 356 for i in pairs(data) do |
343 if not i is number then | 357 if not i is number then |
344 return false | 358 return false |
345 end | 359 end |
346 end | 360 end |
347 | 361 |
348 return type_validators.table(schema, data) | 362 return type_validators.table(schema, data, root) |
349 end | 363 end |
350 return false | 364 return false |
351 end | 365 end |
352 | 366 |
353 json_schema_object.validate = validate; | 367 json_schema_object.validate = validate; |