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;