@@ -277,6 +277,162 @@ Given this schema, the instance `["a", "b", "ccc"]` will fail because `"ccc"` is
277
277
considered unevaluated and fails the ` unevaluatedItems ` keyword like it did in
278
278
previous drafts.
279
279
280
+ ## Embedded Schemas and Bundling
281
+ In Draft 2019-09, the meaning of ` $id ` in a sub-schema changed from indicating a
282
+ base URI change within the current schema to indicating an embedded schema
283
+ independent of the parent schema. A schema that contains one or more embedded
284
+ schemas is called a "Compound Schema Document". This draft introduces guidance
285
+ on how bundlers should embedded schemas to create Compound Schema Documents.
286
+
287
+ If you reference an external schema, that schema can declare it's own ` $schema `
288
+ and that may be different than the ` $schema ` of the referencing schema.
289
+ Implementations need to be prepared to switch processing modes or throw an
290
+ error if they don't support the ` $schema ` of the referenced schema. Embedded
291
+ schemas work exactly the same way. They may declare a ` $schema ` that is not the
292
+ same as the parent schema and implementations need to be prepared to handle the
293
+ ` $schema ` change appropriately.
294
+
295
+ A notable consequence of embedded schemas having a different ` $schema ` than its
296
+ parent is that implementations can't validate Compound Schema Documents directly
297
+ against the meta-schema. The Compound Schema Document needs to be decomposed and
298
+ each Schema Resource needs to be validated individually against the appropriate
299
+ meta-schema for that schema.
300
+
301
+ This draft introduces official guidance on how to use embedded schemas to
302
+ bundle schemas into a Compound Schema Document. The approach is designed to not
303
+ have to modify schemas (other than adding to ` $defs ` ) so that output results
304
+ remain as similar as possible whether you are validating the bundled schema or
305
+ following external references. Here's an example of a customer schema with
306
+ external references that we want to bundle.
307
+
308
+ ``` json
309
+ {
310
+ "$schema" : " https://json-schema.org/draft/2020-12" ,
311
+ "$id" : " https://example.com/schema/customer" ,
312
+
313
+ "type" : " object" ,
314
+ "properties" : {
315
+ "name" : { "type" : " string" },
316
+ "phone" : { "$ref" : " /schema/common#/$defs/phone" },
317
+ "address" : { "$ref" : " /schema/address" }
318
+ }
319
+ }
320
+ ```
321
+
322
+ ``` json
323
+ {
324
+ "$schema" : " https://json-schema.org/draft/2020-12" ,
325
+ "$id" : " https://example.com/schema/address" ,
326
+
327
+ "type" : " object" ,
328
+ "properties" : {
329
+ "address" : { "type" : " string" },
330
+ "city" : { "type" : " string" },
331
+ "postalCode" : { "type" : " /schema/common#/$defs/usaPostalCode" },
332
+ "state" : { "type" : " /$defs/states" }
333
+ },
334
+
335
+ "$defs" : {
336
+ "states" : {
337
+ "enum" : [... ]
338
+ }
339
+ }
340
+ }
341
+ ```
342
+
343
+ ``` json
344
+ {
345
+ "$schema" : " https://json-schema.org/draft/2019-09" ,
346
+ "$id" : " https://example.com/schema/common" ,
347
+
348
+ "$defs" : {
349
+ "phone" : {
350
+ "type" : " string" ,
351
+ "pattern" : " ^[\+ ]?[(]?[0-9]{3}[)]?[-\s\. ]?[0-9]{3}[-\s\. ]?[0-9]{4,6}$"
352
+ },
353
+ "usaPostalCode" : {
354
+ "type" : " string" ,
355
+ "pattern" : " ^[0-9]{5}(?:-[0-9]{4})?$"
356
+ },
357
+ "unsignedInt" : {
358
+ "type" : " integer" ,
359
+ "minimum" : 0
360
+ }
361
+ }
362
+ }
363
+ ```
364
+
365
+ To bundle these schemas, we simply add each of the referenced schemas as
366
+ embedded schemas using ` $defs ` . Here's what the bundled schema would look like.
367
+
368
+ ``` json
369
+ {
370
+ "$schema" : " https://json-schema.org/draft/2020-12" ,
371
+ "$id" : " https://example.com/schema/customer" ,
372
+
373
+ "type" : " object" ,
374
+ "properties" : {
375
+ "name" : { "type" : " string" },
376
+ "phone" : { "$ref" : " /schema/common#/$defs/phone" },
377
+ "address" : { "$ref" : " /schema/address" }
378
+ }
379
+
380
+ "$defs" : {
381
+ "https://example.com/schema/address" : {
382
+ "$id" : " https://example.com/schema/address" ,
383
+
384
+ "type" : " object" ,
385
+ "properties" : {
386
+ "address" : { "type" : " string" },
387
+ "city" : { "type" : " string" },
388
+ "postalCode" : { "type" : " /schema/common#/$defs/usaPostalCode" },
389
+ "state" : { "type" : " #/$defs/states" }
390
+ },
391
+
392
+ "$defs" : {
393
+ "states" : {
394
+ "enum" : [... ]
395
+ }
396
+ }
397
+ },
398
+ "$id" : "https://example.com/schema/common" : {
399
+ "$schema" : " https://json-schema.org/draft/2019-09" ,
400
+ "$id" : " https://example.com/schema/common" ,
401
+
402
+ "$defs" : {
403
+ "phone" : {
404
+ "type" : " string" ,
405
+ "pattern" : " ^[\+ ]?[(]?[0-9]{3}[)]?[-\s\. ]?[0-9]{3}[-\s\. ]?[0-9]{4,6}$"
406
+ },
407
+ "usaPostalCode" : {
408
+ "type" : " string" ,
409
+ "pattern" : " ^[0-9]{5}(?:-[0-9]{4})?$"
410
+ },
411
+ "unsignedInt" : {
412
+ "type" : " integer" ,
413
+ "minimum" : 0
414
+ }
415
+ }
416
+ }
417
+ }
418
+ }
419
+ ```
420
+
421
+ Here are a few things you might notice from this example.
422
+
423
+ 1 . No ` $ref ` s were modified. Even local references are unchanged.
424
+ 2 . ` https://example.com/schema/common#/$defs/unsignedInt ` got pulled in with the
425
+ common schema even though it isn't used. It's allowed to trim out the extra
426
+ definitions, but not necessary.
427
+ 3 . ` https://example.com/schema/address ` doesn't declare a ` $schema ` . Because it
428
+ uses the same ` $schema ` as ` https://example.com/schema/customer ` , it can skip
429
+ that declaration and use the ` $schema ` from the schema it's embedded in.
430
+ 4 . ` https://example.com/schema/common ` uses a different ` $schema ` than the
431
+ document it's embedded in. That's allowed.
432
+ 5 . Definitions from ` https://example.com/schema/common ` are used in both of the
433
+ other schemas and only needs to be included once. It isn't necessary for
434
+ bundlers to embed a schema inside another embedded schema.
435
+
280
436
## Vocabulary Changes
281
437
The ` unevaluatedProperties ` and ` unevaluatedItems ` keywords have been moved from
282
438
the applicator vocabulary to their own vocabulary designated which is required
0 commit comments