idnits 2.17.1 draft-ucarion-json-type-definition-03.txt: Checking boilerplate required by RFC 5378 and the IETF Trust (see https://trustee.ietf.org/license-info): ---------------------------------------------------------------------------- No issues found here. Checking nits according to https://www.ietf.org/id-info/1id-guidelines.txt: ---------------------------------------------------------------------------- No issues found here. Checking nits according to https://www.ietf.org/id-info/checklist : ---------------------------------------------------------------------------- No issues found here. Miscellaneous warnings: ---------------------------------------------------------------------------- == The copyright year in the IETF Trust and authors Copyright Line does not match the current year -- The document date (April 26, 2020) is 1460 days in the past. Is this intentional? Checking references for intended status: Experimental ---------------------------------------------------------------------------- -- Looks like a reference, but probably isn't: '1' on line 1369 -- Looks like a reference, but probably isn't: '2' on line 1369 -- Looks like a reference, but probably isn't: '3' on line 1369 Summary: 0 errors (**), 0 flaws (~~), 1 warning (==), 4 comments (--). Run idnits with the --verbose option for more detailed information about the items above. -------------------------------------------------------------------------------- 2 Independent Submission U. Carion 3 Internet-Draft Segment 4 Intended status: Experimental April 26, 2020 5 Expires: October 28, 2020 7 JSON Type Definition 8 draft-ucarion-json-type-definition-03 10 Abstract 12 This document proposes a format, called JSON Type Definition (JTD), 13 for describing the shape of JavaScript Object Notation (JSON) 14 messages. Its main goals are to enable code generation from schemas 15 as well as portable validation with standardized error indicators. 16 To this end, JTD is intentionally limited to be no more expressive 17 than the type systems of mainstream programming languages. This 18 intentional limitation, as well as the decision to make JTD schemas 19 be JSON documents, makes tooling atop of JTD easier to build. 21 This document does not have IETF consensus and is presented here to 22 facilitate experimentation with the concept of JTD. 24 Status of This Memo 26 This Internet-Draft is submitted in full conformance with the 27 provisions of BCP 78 and BCP 79. 29 Internet-Drafts are working documents of the Internet Engineering 30 Task Force (IETF). Note that other groups may also distribute 31 working documents as Internet-Drafts. The list of current Internet- 32 Drafts is at https://datatracker.ietf.org/drafts/current/. 34 Internet-Drafts are draft documents valid for a maximum of six months 35 and may be updated, replaced, or obsoleted by other documents at any 36 time. It is inappropriate to use Internet-Drafts as reference 37 material or to cite them other than as "work in progress." 39 This Internet-Draft will expire on October 28, 2020. 41 Copyright Notice 43 Copyright (c) 2020 IETF Trust and the persons identified as the 44 document authors. All rights reserved. 46 This document is subject to BCP 78 and the IETF Trust's Legal 47 Provisions Relating to IETF Documents 48 (https://trustee.ietf.org/license-info) in effect on the date of 49 publication of this document. Please review these documents 50 carefully, as they describe your rights and restrictions with respect 51 to this document. Code Components extracted from this document must 52 include Simplified BSD License text as described in Section 4.e of 53 the Trust Legal Provisions and are provided without warranty as 54 described in the Simplified BSD License. 56 Table of Contents 58 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . 3 59 1.1. Terminology . . . . . . . . . . . . . . . . . . . . . . . 5 60 1.2. Scope of Experiment . . . . . . . . . . . . . . . . . . . 5 61 2. Syntax . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 62 2.1. Root vs. non-root schemas . . . . . . . . . . . . . . . . 9 63 2.2. Forms . . . . . . . . . . . . . . . . . . . . . . . . . . 9 64 2.2.1. Empty . . . . . . . . . . . . . . . . . . . . . . . . 9 65 2.2.2. Ref . . . . . . . . . . . . . . . . . . . . . . . . . 10 66 2.2.3. Type . . . . . . . . . . . . . . . . . . . . . . . . 11 67 2.2.4. Enum . . . . . . . . . . . . . . . . . . . . . . . . 11 68 2.2.5. Elements . . . . . . . . . . . . . . . . . . . . . . 12 69 2.2.6. Properties . . . . . . . . . . . . . . . . . . . . . 13 70 2.2.7. Values . . . . . . . . . . . . . . . . . . . . . . . 14 71 2.2.8. Discriminator . . . . . . . . . . . . . . . . . . . . 15 72 2.3. Extending JTD's Syntax . . . . . . . . . . . . . . . . . 17 73 3. Semantics . . . . . . . . . . . . . . . . . . . . . . . . . . 18 74 3.1. Allowing Additional Properties . . . . . . . . . . . . . 18 75 3.2. Errors . . . . . . . . . . . . . . . . . . . . . . . . . 19 76 3.3. Forms . . . . . . . . . . . . . . . . . . . . . . . . . . 20 77 3.3.1. Empty . . . . . . . . . . . . . . . . . . . . . . . . 20 78 3.3.2. Ref . . . . . . . . . . . . . . . . . . . . . . . . . 20 79 3.3.3. Type . . . . . . . . . . . . . . . . . . . . . . . . 22 80 3.3.4. Enum . . . . . . . . . . . . . . . . . . . . . . . . 27 81 3.3.5. Elements . . . . . . . . . . . . . . . . . . . . . . 28 82 3.3.6. Properties . . . . . . . . . . . . . . . . . . . . . 30 83 3.3.7. Values . . . . . . . . . . . . . . . . . . . . . . . 34 84 3.3.8. Discriminator . . . . . . . . . . . . . . . . . . . . 36 85 4. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 42 86 5. Security Considerations . . . . . . . . . . . . . . . . . . . 43 87 6. References . . . . . . . . . . . . . . . . . . . . . . . . . 43 88 6.1. Normative References . . . . . . . . . . . . . . . . . . 43 89 6.2. Informative References . . . . . . . . . . . . . . . . . 44 90 Appendix A. Rationale for Omitted Features . . . . . . . . . . . 44 91 A.1. Support for 64-bit Numbers . . . . . . . . . . . . . . . 44 92 A.2. Support for Non-Root Definitions . . . . . . . . . . . . 45 93 Appendix B. Comparison with CDDL . . . . . . . . . . . . . . . . 46 94 Appendix C. Example . . . . . . . . . . . . . . . . . . . . . . 49 95 Acknowledgments . . . . . . . . . . . . . . . . . . . . . . . . . 50 96 Author's Address . . . . . . . . . . . . . . . . . . . . . . . . 51 98 1. Introduction 100 This document describes a schema language for JSON [RFC8259] called 101 JSON Type Definition (JTD). 103 There exist many options for describing JSON data. JTD's niche is to 104 focus on enabling code generation from schemas; to this end, JTD's 105 expressiveness is intentionally limited to be no more powerful than 106 what can be expressed in the type systems of mainstream programming 107 languages. 109 The goals of JTD are to: 111 o Provide an unambiguous description of the overall structure of a 112 JSON document. 114 o Be able to describe common JSON datatypes and structures. That 115 is, the datatypes and structures necessary to support most JSON 116 documents, and which are widely understood in an interoperable way 117 by JSON implementations. 119 o Provide a single format that is readable and editable by both 120 humans and machines, and which can be embedded within other JSON 121 documents. This makes JTD a convenient format for tooling to 122 accept as input or produce as output. 124 o Enable code generation from JTD schemas. JTD schemas are meant to 125 be easy to convert into data structures idiomatic to mainstream 126 programming languages. 128 o Provide a standardized format for error indicators when data does 129 not conform with a schema. 131 JTD is intentionally designed as a rather minimal schema language. 132 Thus, although JTD can describe JSON, it is not able to describe its 133 own structure: this document uses Concise Data Definition Language 134 (CDDL) [RFC8610] to describe JTD's syntax. By keeping the 135 expressiveness of the schema language minimal, JTD makes code 136 generation and standardized error indicators easier to implement. 138 Examples in this document use constructs from the C++ programming 139 language. These examples are provided to aid the reader in 140 understanding the principles of JTD, but are not limiting in any way. 142 JTD's feature set is designed to represent common patterns in JSON- 143 using applications, while still having a clear correspondence to 144 programming languages in widespread use. Thus, JTD supports: 146 o Signed and unsigned 8, 16, and 32-bit integers. A tool which 147 converts JTD schemas into code can use "int8_t", "uint8_t", 148 "int16_t", etc., or their equivalents in the target language, to 149 represent these JTD types. 151 o A distinction between "float32" and "float64". Code generators 152 can use "float" and "double", or their equivalents, for these JTD 153 types. 155 o A "properties" form of JSON objects, corresponding to some sort of 156 struct or record. The "properties" form of JSON objects is akin 157 to a C++ "struct". 159 o A "values" form of JSON objects, corresponding to some sort of 160 dictionary or associative array. The "values" form of JSON 161 objects is akin to a C++ "std::map". 163 o A "discriminator" form of JSON objects, corresponding to a 164 discriminated (or "tagged") union. The "discriminator" form of 165 JSON objects is akin to a C++ "std::variant". 167 The principle of common patterns in JSON is why JTD does not support 168 64-bit integers, as these are usually transmitted over JSON in a non- 169 interoperable (i.e., ignoring the recommendations in Section 2.2 of 170 [RFC7493]) or mutually inconsistent ways. Appendix A.1 further 171 elaborates on why JTD does not support 64-bit integers. 173 The principle of clear correspondence to common programming languages 174 is why JTD does not support, for example, a data type for numbers up 175 to 2**53-1. 177 It is expected that for many use-cases, a schema language of JTD's 178 expressiveness is sufficient. Where a more expressive language is 179 required, alternatives exist in CDDL and others. 181 This document does not have IETF consensus and is presented here to 182 facilitate experimentation with the concept of JTD. The purpose of 183 the experiment is to gain experience with JTD and to possibly revise 184 this work accordingly. If JTD is determined to be a valuable and 185 popular approach it may be taken to the IETF for further discussion 186 and revision. 188 This document has the following structure: 190 Section 2 defines the syntax of JTD. Section 3 describes the 191 semantics of JTD; this includes determining whether some data 192 satisfies a schema and what error indicators should be produced when 193 the data is unsatisfactory. Appendix A discusses why certain 194 features are omitted from JTD. Appendix B presents various JTD 195 schemas and their CDDL equivalents. 197 1.1. Terminology 199 The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", 200 "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and 201 "OPTIONAL" in this document are to be interpreted as described in 202 BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all 203 capitals, as shown here. These words may also appear in this 204 document in lower case as plain English words, absent their normative 205 meanings. 207 The term "JSON Pointer", when it appears in this document, is to be 208 understood as it is defined in [RFC6901]. 210 The terms "object", "member", "array", "number", "name", and "string" 211 in this document are to be interpreted as described in [RFC8259]. 213 The term "instance", when it appears in this document, refers to a 214 JSON value being validated against a JTD schema. 216 1.2. Scope of Experiment 218 JTD is an experiment. Participation in this experiment consists of 219 using JTD to validate or document interchanged JSON messages, or in 220 building tooling atop of JTD. Feedback on the results of this 221 experiment may be e-mailed to the author. Participants in this 222 experiment are anticipated to mostly be nodes that provide or consume 223 JSON-based APIs. 225 Nodes know if they are participating in the experiment if they are 226 validating JSON messages against a JTD schema, or if they are relying 227 on another node to do so. Nodes are also participating in the 228 experiment if they are running code generated from a JTD schema. 230 The risk of this experiment "escaping" takes the form of a JTD- 231 supporting node expecting another node, which lacks such support, to 232 validate messages against some JTD schema. In such a case, the 233 outcome will likely be that the nodes fail to interchange information 234 correctly. 236 This experiment will be deemed successful when JTD has been 237 implemented by multiple independent parties, and these parties 238 successfully use JTD to facilitate information interchange within 239 their internal systems or between systems operated by independent 240 parties. 242 If this experiment is deemed successful, and JTD is determined to be 243 a valuable and popular approach, it may be taken to the IETF for 244 further discussion and revision. One possible outcome of this 245 discussion and revision could be that a working group produces a 246 Standards Track specification of JTD. 248 Some implementations of JTD, as well as code generators and other 249 tooling related to JTD, are available at . 252 2. Syntax 254 This section describes when a JSON document is a correct JTD schema. 255 Because Concise Data Definition Language (CDDL) is well-suited to the 256 task of defining complex JSON formats, such as JTD schemas, this 257 section uses CDDL to describe the format of JTD schemas. 259 JTD schemas may recursively contain other schemas. In this document, 260 a "root schema" is one which is not contained within another schema, 261 i.e. it is "top-level". 263 A JTD schema is a JSON object taking on an appropriate form. JTD 264 schemas may contain "additional data", discussed in Section 2.3. 265 Root JTD schemas may optionally contain definitions (a mapping from 266 names to schemas). 268 A correct root JTD schema MUST match the "root-schema" CDDL rule 269 described in this section. A correct non-root JTD schema MUST match 270 the "schema" CDDL rule described in this section. 272 ; root-schema is identical to schema, but additionally allows for 273 ; definitions. 274 ; 275 ; definitions are prohibited from appearing on non-root schemas. 276 root-schema = { 277 ? definitions: { * tstr => { schema}}, 278 schema, 279 } 281 ; schema is the main CDDL rule defining a JTD schema. 282 ; 283 ; All JTD schemas are JSON objects taking on one of eight forms 284 ; listed here. 285 schema = ( 286 ref // 287 type // 288 enum // 289 elements // 290 properties // 291 values // 292 discriminator // 293 empty // 294 ) 296 ; shared is a CDDL rule containing properties that all eight schema 297 ; forms share. 298 shared = ( 299 ? metadata: { * tstr => any }, 300 ? nullable: bool, 301 ) 303 ; empty describes the "empty" schema form. 304 empty = shared 306 ; ref describes the "ref" schema form. 307 ; 308 ; There are additional constraints on this form that cannot be 309 ; expressed in CDDL. Section 2.2.2 describes these additional 310 ; constraints in detail. 311 ref = ( ref: tstr, shared ) 313 ; type describes the "type" schema form. 314 type = ( 315 type: "boolean" 316 / "float32" 317 / "float64" 318 / "int8" 319 / "uint8" 320 / "int16" 321 / "uint16" 322 / "int32" 323 / "uint32" 324 / "string" 325 / "timestamp", 326 shared, 327 ) 329 ; enum describes the "enum" schema form. 330 ; 331 ; There are additional constraints on this form that cannot be 332 ; expressed in CDDL. Section 2.2.4 describes these additional 333 ; constraints in detail. 334 enum = ( enum: [+ tstr], shared ) 336 ; elements describes the "elements" schema form. 337 elements = ( elements: { schema }, shared ) 338 ; properties describes the "properties" schema form. 339 ; 340 ; This CDDL rule is defined so that a schema of the "properties" form 341 ; may omit a member named "properties" or a member named 342 ; "optionalProperties", but not both. 343 ; 344 ; There are additional constraints on this form that cannot be 345 ; expressed in CDDL. Section 2.2.6 describes these additional 346 ; constraints in detail. 347 properties = (with-properties // with-optional-properties) 349 with-properties = ( 350 properties: { * tstr => { schema }}, 351 ? optionalProperties: { * tstr => { schema }}, 352 ? additionalProperties: bool, 353 shared, 354 ) 356 with-optional-properties = ( 357 ? properties: { * tstr => { schema }}, 358 optionalProperties: { * tstr => { schema }}, 359 ? additionalProperties: bool, 360 shared, 361 ) 363 ; values describes the "values" schema form. 364 values = ( values: { schema }, shared ) 366 ; discriminator describes the "discriminator" schema form. 367 ; 368 ; There are additional constraints on this form that cannot be 369 ; expressed in CDDL. Section 2.2.8 describes these additional 370 ; constraints in detail. 371 discriminator = ( 372 discriminator: tstr, 374 ; Note well: this rule is defined in terms of the "properties" 375 ; CDDL rule, not the "schema" CDDL rule. 376 mapping: { * tstr => { properties } } 377 shared, 378 ) 380 Figure 1: CDDL definition of a schema 382 The remainder of this section will describe constraints on JTD 383 schemas which cannot be expressed in CDDL, and will provide examples 384 of valid and invalid JTD schemas. 386 2.1. Root vs. non-root schemas 388 The "root-schema" rule in Figure 1 permits for a member named 389 "definitions", but the "schema" rule does not permit for such a 390 member. This means that only root (i.e., "top-level") JTD schemas 391 can have a "definitions" object, and sub-schemas may not. 393 Thus 395 { "definitions": {} } 397 is a correct JTD schema, but 399 { 400 "definitions": { 401 "foo": { 402 "definitions": {} 403 } 404 } 405 } 407 is not, because sub-schemas (such as the object at "/definitions/ 408 foo") must not have a member named "definitions". 410 2.2. Forms 412 JTD schemas (i.e. JSON objects satisfying the "schema" CDDL rule in 413 Figure 1) must take on one of eight forms. These forms are defined 414 so as to be mutually exclusive; a schema cannot satisfy multiple 415 forms at once. 417 2.2.1. Empty 419 The "empty" form is defined by the "empty" CDDL rule in Figure 1. 420 The semantics of the "empty" form are described in Section 3.3.1. 422 Despite the name "empty", schemas of the "empty" form are not 423 necessarily empty JSON objects. Like schemas of any of the eight 424 forms, schemas of the "empty" form may contain members named 425 "nullable" (whose value must be "true" or "false") or "metadata" 426 (whose value must be an object) or both. 428 Thus 430 {} 432 and 433 { "nullable": true } 435 and 437 { "nullable": true, "metadata": { "foo": "bar" }} 439 are correct JTD schemas of the empty form, but 441 { "nullable": "foo" } 443 is not, because the value of the member named "nullable" must be 444 "true" or "false". 446 2.2.2. Ref 448 The "ref" form is defined by the "ref" CDDL rule in Figure 1. The 449 semantics of the "ref" form are described in Section 3.3.2. 451 For a schema of the "ref" form to be correct, the value of the member 452 named "ref" must refer to one of the definitions found at the root 453 level of the schema it appears in. More formally, for a schema _S_ 454 of the "ref" form: 456 o Let _B_ be the root schema containing the schema, or the schema 457 itself if it is a root schema. 459 o Let _R_ be the value of the member of _S_ with the name "ref". 461 If the schema is correct, then _B_ MUST have a member _D_ with the 462 name "definitions", and _D_ MUST contain a member whose name equals 463 _R_. 465 Thus 467 { 468 "definitions": { 469 "coordinates": { 470 "properties": { 471 "lat": { "type": "float32" }, 472 "lng": { "type": "float32" } 473 } 474 } 475 }, 476 "properties": { 477 "user_location": { "ref": "coordinates" }, 478 "server_location": { "ref": "coordinates" } 479 } 480 } 482 is a correct JTD schema, and demonstrates the point of the "ref" 483 form: to avoid re-defining the same thing twice. However, 485 { "ref": "foo" } 487 is not a correct JTD schema, as there is no top-level "definitions", 488 and so the "ref" form cannot be correct. Similarly, 490 { "definitions": { "foo": {}}, "ref": "bar" } 492 is not a correct JTD schema, as there is no member named "bar" in the 493 top-level "definitions". 495 2.2.3. Type 497 The "type" form is defined by the "type" CDDL rule in Figure 1. The 498 semantics of the "type" form are described in Section 3.3.3. 500 As an example of a correct JTD schema of the "type" form, 502 { "type": "uint8" } 504 is a correct JTD schema, whereas 506 { "type": true } 508 and 510 { "type": "foo" } 512 are not correct schemas, as neither "true" nor the JSON string "foo" 513 are in the list of permitted values of the "type" member described in 514 the "type" CDDL rule in Figure 1. 516 2.2.4. Enum 518 The "enum" form is defined by the "enum" CDDL rule in Figure 1. The 519 semantics of the "enum" form are described in Section 3.3.4. 521 For a schema of the "enum" form to be correct, the value of the 522 member named "enum" must be a nonempty array of strings, and that 523 array must not contain duplicate values. More formally, for a schema 524 _S_ of the "enum" form: 526 o Let _E_ be the value of the member of _S_ with name "enum". 528 If the schema is correct, then there MUST NOT exist any pair of 529 elements of _E_ which encode equal string values, where string 530 equality is defined as in Section 8.3 of [RFC8259]. 532 Thus 534 { "enum": [] } 536 is not a correct JTD schema, as the value of the member named "enum" 537 must be nonempty, and 539 { "enum": ["a\\b", "a\u005Cb"] } 541 is not a correct JTD schema, as 543 "a\\b" 545 and 547 "a\u005Cb" 549 encode strings that are equal by the definition of string equality 550 given in Section 8.3 of [RFC8259]. By contrast, 552 { "enum": ["PENDING", "IN_PROGRESS", "DONE" ]} 554 is an example of a correct JTD schema of the "enum" form. 556 2.2.5. Elements 558 The "elements" form is defined by the "elements" CDDL rule in 559 Figure 1. The semantics of the "elements" form are described in 560 Section 3.3.5. 562 As an example of a correct JTD schema of the "elements" form, 564 { "elements": { "type": "uint8" }} 566 is a correct JTD schema, whereas 568 { "elements": true } 570 and 572 { "elements": { "type": "foo" } } 574 are not correct schemas, as neither 575 true 577 nor 579 { "type": "foo" } 581 are correct JTD schemas, and the value of the member named "elements" 582 must be a correct JTD schema. 584 2.2.6. Properties 586 The "properties" form is defined by the "properties" CDDL rule in 587 Figure 1. The semantics of the "properties" form are described in 588 Section 3.3.6. 590 For a schema of the "properties" form to be correct, properties must 591 either be required (i.e., in "properties") or optional (i.e., in 592 "optionalProperties"), but not both. More formally: 594 If a schema has both a member named "properties" (with value _P_) and 595 another member named "optionalProperties" (with value _O_), then _O_ 596 and _P_ MUST NOT have any member names in common; that is, no member 597 of _P_ may have a name equal to the name of any member of _O_, under 598 the definition of string equality given in Section 8.3 of [RFC8259]. 600 Thus 602 { 603 "properties": { "confusing": {} }, 604 "optionalProperties": { "confusing": {} } 605 } 607 is not a correct JTD schema, as "confusing" appears in both 608 "properties" and "optionalProperties". By contrast, 609 { 610 "properties": { 611 "users": { 612 "elements": { 613 "properties": { 614 "id": { "type": "string" }, 615 "name": { "type": "string" }, 616 "create_time": { "type": "timestamp" } 617 }, 618 "optionalProperties": { 619 "delete_time": { "type": "timestamp" } 620 } 621 } 622 }, 623 "next_page_token": { "type": "string" } 624 } 625 } 627 is a correct JTD schema of the "properties" form, describing a 628 paginated list of users and demonstrating the recursive nature of the 629 syntax of JTD schemas. 631 2.2.7. Values 633 The "values" form is defined by the "values" CDDL rule in Figure 1. 634 The semantics of the "values" form are described in Section 3.3.7. 636 As an example of a correct JTD schema of the "values" form, 638 { "values": { "type": "uint8" }} 640 is a correct JTD schema, whereas 642 { "values": true } 644 and 646 { "values": { "type": "foo" } } 648 are not correct schemas, as neither 650 true 652 nor 654 { "type": "foo" } 656 are correct JTD schemas, and the value of the member named "values" 657 must be a correct JTD schema. 659 2.2.8. Discriminator 661 The "discriminator" form is defined by the "discriminator" CDDL rule 662 in Figure 1. The semantics of the "discriminator" form are described 663 in Section 3.3.8. Understanding the semantics of the "discriminator" 664 form will likely aid the reader in understanding why this section 665 provides constraints on the "discriminator" form beyond those in 666 Figure 1. 668 To prevent ambiguous or unsatisfiable constraints on the 669 "discriminator" property of a tagged union, an additional constraint 670 on schemas of the "discriminator" form exists. For schemas of the 671 discriminator form: 673 o Let _D_ be the member of the schema with the name "discriminator". 675 o Let _M_ be the member of the schema with the name "mapping". 677 If the schema is correct, then all member values _S_ of _M_ will be 678 schemas of the "properties" form. For each _S_: 680 o If _S_ has a member _N_ whose name equals "nullable", _N_'s value 681 MUST NOT be the JSON primitive value "true". 683 o For each member _P_ of _S_ whose name equals "properties" or 684 "optionalProperties", _P_'s value, which must be an object, MUST 685 NOT contain any members whose name equals _D_'s value. 687 Thus 689 { 690 "discriminator": "event_type", 691 "mapping": { 692 "can_the_object_be_null_or_not?": { 693 "nullable": true, 694 "properties": { "foo": { "type": "string" } }} 695 } 696 } 697 } 699 is an incorrect schema, as a member of "mapping" has a member named 700 "nullable" whose value is "true". This would suggest that the 701 instance may be null. Yet the top-level schema lacks such a 702 "nullable" set to "true", which would suggest that the instance in 703 fact cannot be null. If this were a correct JTD schema, it would be 704 unclear which piece of information takes "precedence". 706 JTD handles such possible ambiguity by disallowing, at the syntactic 707 level, the possibility of contradictory specifications of whether an 708 instance described by a schema of the "discriminator" form may be 709 null. The schemas in a discriminator "mapping" cannot have 710 "nullable" set to "true"; only the discriminator itself can use 711 "nullable" in this way. 713 It also follows that 715 { 716 "discriminator": "event_type", 717 "mapping": { 718 "is_event_type_a_string_or_a_float32?": { 719 "properties": { "event_type": { "type": "float32" }} 720 } 721 } 722 } 724 and 726 { 727 "discriminator": "event_type", 728 "mapping": { 729 "is_event_type_a_string_or_an_optional_float32?": { 730 "optionalProperties": { "event_type": { "type": "float32" }} 731 } 732 } 733 } 735 are incorrect schemas, as "event_type" is both the value of 736 "discriminator" and a member name in one of the "mapping" member 737 "properties" or "optionalProperties". This is ambiguous, because 738 ordinarily the "discriminator" keyword would indicate that 739 "event_type" is expected to be a string, but another part of the 740 schema specifies that "event_type" is expected to be a number. 742 JTD handles such possible ambiguity by disallowing, at the syntactic 743 level, the possibility of contradictory specifications of 744 discriminator "tags". Discriminator "tags" cannot be re-defined in 745 other parts of the schema. 747 By contrast, 748 { 749 "tag": "event_type", 750 "mapping": { 751 "account_deleted": { 752 "properties": { 753 "account_id": { "type": "string" } 754 } 755 }, 756 "account_payment_plan_changed": { 757 "properties": { 758 "account_id": { "type": "string" }, 759 "payment_plan": { "enum": ["FREE", "PAID"] } 760 }, 761 "optionalProperties": { 762 "upgraded_by": { "type": "string" } 763 } 764 } 765 } 766 } 768 is a correct schema, describing a pattern of data common in JSON- 769 based messaging systems. Section 3.3.8 provides examples of what 770 this schema accepts and rejects. 772 2.3. Extending JTD's Syntax 774 This document does not describe any extension mechanisms for JTD 775 schema validation, which is described in Section 3. However, schemas 776 are defined to optionally contain a "metadata" keyword, whose value 777 is an arbitrary JSON object. Call the members of this object 778 "metadata members". 780 Users MAY add metadata members to JTD schemas to convey information 781 that is not pertinent to validation. For example, such metadata 782 members could provide hints to code generators, or trigger some 783 special behavior for a library that generates user interfaces from 784 schemas. 786 Users SHOULD NOT expect metadata members to be understood by other 787 parties. As a result, if consistent validation with other parties is 788 a requirement, users SHOULD NOT use metadata members to affect how 789 schema validation, as described in Section 3, works. 791 Users MAY expect metadata members to be understood by other parties, 792 and MAY use metadata members to affect how schema validation works, 793 if these other parties are somehow known to support these metadata 794 members. For example, two parties may agree, out of band, that they 795 will support an extended JTD with a custom metadata member that 796 affects validation. 798 3. Semantics 800 This section describes when an instance is valid against a correct 801 JTD schema, and the error indicators to produce when an instance is 802 invalid. 804 3.1. Allowing Additional Properties 806 Users will have different desired behavior with respect to 807 "unspecified" members in an instance. For example, consider the JTD 808 schema in Figure 2: 810 { "properties": { "a": { "type": "string" }}} 812 Figure 2: An illustrative JTD schema 814 Some users may expect that 816 {"a": "foo", "b": "bar"} 818 satisfies the schema in Figure 2. Others may disagree, as "b" is not 819 one of the properties described in the schema. In this document, 820 allowing such "unspecified" members, like "b" in this example, 821 happens when evaluation is in "allow additional properties" mode. 823 Evaluation of a schema does not allow additional properties by 824 default, but can be overridden by having the schema include a member 825 named "additionalProperties", where that member has a value of 826 "true". 828 More formally: evaluation of a schema _S_ is in "allow additional 829 properties" mode if there exists a member of _S_ whose name equals 830 "additionalProperties", and whose value is a boolean "true". 831 Otherwise, evaluation of _S_ is not in "allow additional properties" 832 mode. 834 See Section 3.3.6 for how allowing unknown properties affects schema 835 evaluation, but briefly, the schema 837 { "properties": { "a": { "type": "string" }}} 839 rejects 841 { "a": "foo", "b": "bar" } 843 However, the schema 845 { 846 "additionalProperties": true, 847 "properties": { "a": { "type": "string" }} 848 } 850 accepts 852 { "a": "foo", "b": "bar" } 854 Note that "additionalProperties" does not get "inherited" by sub- 855 schemas. For example, the JTD schema 857 { 858 "additionalProperties": true, 859 "properties": { 860 "a": { 861 "properties": { 862 "b": { "type": "string" } 863 } 864 } 865 } 866 } 868 accepts 870 { "a": { "b": "c" }, "foo": "bar" } 872 but rejects 874 { "a": { "b": "c", "foo": "bar" }} 876 because the "additionalProperties" at the root level does not affect 877 the behavior of sub-schemas. 879 Note from Figure 1 that only schemas of the "properties" form may 880 have a member named "additionalProperties". 882 3.2. Errors 884 To facilitate consistent validation error handling, this document 885 specifies a standard error indicator format. Implementations SHOULD 886 support producing error indicators in this standard form. 888 The standard error indicator format is a JSON array. The order of 889 the elements of this array is not specified. The elements of this 890 array are JSON objects with: 892 o A member with the name "instancePath", whose value is a JSON 893 string encoding a JSON Pointer. This JSON Pointer will point to 894 the part of the instance that was rejected. 896 o A member with the name "schemaPath", whose value is a JSON string 897 encoding a JSON Pointer. This JSON Pointer will point to the part 898 of the schema that rejected the instance. 900 The values for "instancePath" and "schemaPath" depend on the form of 901 the schema, and are described in detail in Section 3.3. 903 3.3. Forms 905 This section describes, for each of the eight JTD schema forms, the 906 rules dictating whether an instance is accepted, as well as the error 907 indicators to produce when an instance is invalid. 909 The forms a correct schema may take on are formally described in 910 Section 2. 912 3.3.1. Empty 914 The "empty" form is meant to describe instances whose values are 915 unknown, unpredictable, or otherwise unconstrained by the schema. 916 The syntax of the "empty" form is described in Section 2.2.1. 918 If a schema is of the empty form, then it accepts all instances. A 919 schema of the empty form will never produce any error indicators. 921 3.3.2. Ref 923 The "ref" form is for when a schema is defined in terms of something 924 in the "definitions" of the root schema. The ref form enables 925 schemas to be less repetitive, and also enables describing recursive 926 structures. The syntax of the "ref" form is described in 927 Section 2.2.2. 929 If a schema is of the ref form, then: 931 o If the schema has a member named "nullable" whose value is the 932 boolean "true", and the instance is the JSON primitive value 933 "null", then the schema accepts the instance. Otherwise: 935 o Let _B_ be the root schema containing the schema, or the schema 936 itself if it is a root schema. 938 o Let _D_ be the member of _B_ with the name "definitions". By 939 Section 2, _D_ exists. 941 o Let _R_ be the value of the schema member with the name "ref". 943 o Let _S_ be the value of the member of _D_ whose name equals _R_. 944 By Section 2.2.2, _S_ exists, and is a schema. 946 The schema accepts the instance if and only if _S_ accepts the 947 instance. Otherwise, the error indicators to return in this case are 948 the union of the error indicators from evaluating _S_ against the 949 instance. 951 For example, the schema: 953 { 954 "definitions": { "a": { "type": "float32" }}, 955 "ref": "a" 956 } 958 accepts 960 123 962 but rejects 964 null 966 with the error indicator 968 [{ "instancePath": "", "schemaPath": "/definitions/a/type" }] 970 The schema 972 { 973 "definitions": { "a": { "type": "float32" }}, 974 "ref": "a", 975 "nullable": true 976 } 978 accepts 980 null 982 because the schema has a "nullable" member, whose value is "true". 984 Note that "nullable" being "false" has no effect in any of the forms 985 described in this document. For example, the schema 986 { 987 "definitions": { "a": { "nullable": false, "type": "float32" }}, 988 "ref": "a", 989 "nullable": true 990 } 992 accepts 994 null 996 In other words, it is not the case that putting a "false" value for 997 "nullable" will ever "override" a "nullable" member in schemas of the 998 "ref" form; it is correct, though ineffectual, to have a value of 999 "false" for the "nullable" member in a schema. 1001 3.3.3. Type 1003 The "type" form is meant to describe instances whose value is a 1004 boolean, number, string, or timestamp ([RFC3339]). The syntax of the 1005 "type" form is described in Section 2.2.3. 1007 If a schema is of the type form, then: 1009 o If the schema has a member named "nullable" whose value is the 1010 boolean "true", and the instance is the JSON primitive value 1011 "null", then the schema accepts the instance. Otherwise: 1013 o Let _T_ be the value of the member with the name "type". The 1014 following table describes whether the instance is accepted, as a 1015 function of _T_'s value: 1017 +-----------+-------------------------------------------------------+ 1018 | If _T_ | then the instance is accepted if it is ... | 1019 | equals | | 1020 | ... | | 1021 +-----------+-------------------------------------------------------+ 1022 | boolean | equal to "true" or "false" | 1023 | | | 1024 | float32 | a JSON number | 1025 | | | 1026 | float64 | a JSON number | 1027 | | | 1028 | int8 | See Table 2 | 1029 | | | 1030 | uint8 | See Table 2 | 1031 | | | 1032 | int16 | See Table 2 | 1033 | | | 1034 | uint16 | See Table 2 | 1035 | | | 1036 | int32 | See Table 2 | 1037 | | | 1038 | uint32 | See Table 2 | 1039 | | | 1040 | string | a JSON string | 1041 | | | 1042 | timestamp | a JSON string that follows the standard format | 1043 | | described in [RFC3339], as refined by Section 3.3 of | 1044 | | [RFC4287] | 1045 +-----------+-------------------------------------------------------+ 1047 Table 1: Accepted Values for Type 1049 "float32" and "float64" are distinguished from each other in their 1050 intent. "float32" indicates data intended to be processed as an IEEE 1051 754 single-precision float, whereas "float64" indicates data intended 1052 to be processed as an IEEE 754 double-precision float. Tools which 1053 generate code from JTD schemas will likely produce different code for 1054 "float32" than for "float64". 1056 If _T_ starts with "int" or "uint", then the instance is accepted if 1057 and only if it is a JSON number encoding a value with zero fractional 1058 part. Depending on the value of _T_, this encoded number must 1059 additionally fall within a particular range: 1061 +--------+---------------------------+---------------------------+ 1062 | _T_ | Minimum Value (Inclusive) | Maximum Value (Inclusive) | 1063 +--------+---------------------------+---------------------------+ 1064 | int8 | -128 | 127 | 1065 | | | | 1066 | uint8 | 0 | 255 | 1067 | | | | 1068 | int16 | -32,768 | 32,767 | 1069 | | | | 1070 | uint16 | 0 | 65,535 | 1071 | | | | 1072 | int32 | -2,147,483,648 | 2,147,483,647 | 1073 | | | | 1074 | uint32 | 0 | 4,294,967,295 | 1075 +--------+---------------------------+---------------------------+ 1077 Table 2: Ranges for Integer Types 1079 Note that 1081 10 1083 and 1085 10.0 1087 and 1089 1.0e1 1091 encode values with zero fractional part, whereas 1093 10.5 1095 encodes a number with a non-zero fractional part. Thus the schema 1097 {"type": "int8"} 1099 accepts 1101 10 1103 and 1105 10.0 1107 and 1108 1.0e1 1110 but rejects 1112 10.5 1114 as well as 1116 false 1118 because "false" is not a number at all. 1120 If the instance is not accepted, then the error indicator for this 1121 case shall have an "instancePath" pointing to the instance, and a 1122 "schemaPath" pointing to the schema member with the name "type". 1124 For example, the schema: 1126 {"type": "boolean"} 1128 accepts 1130 false 1132 but rejects 1134 127 1136 The schema: 1138 {"type": "float32"} 1140 accepts 1142 10.5 1144 and 1146 127 1148 but rejects 1150 false 1152 The schema: 1154 {"type": "string"} 1156 accepts 1158 "1985-04-12T23:20:50.52Z" 1160 and 1162 "foo" 1164 but rejects 1166 false 1168 The schema: 1170 {"type": "timestamp"} 1172 accepts 1174 "1985-04-12T23:20:50.52Z" 1176 but rejects 1178 "foo" 1180 and 1182 false 1184 The schema: 1186 {"type": "boolean", "nullable": true} 1188 accepts 1190 null 1192 and 1194 false 1196 but rejects 1198 127 1200 In all of the examples of rejected instances given in this section, 1201 the error indicator to produce is: 1203 [{ "instancePath": "", "schemaPath": "/type" }] 1205 3.3.4. Enum 1207 The "enum" form is meant to describe instances whose value must be 1208 one of a given set of string values. The syntax of the "enum" form 1209 is described in Section 2.2.4. 1211 If a schema is of the enum form, then: 1213 o If the schema has a member named "nullable" whose value is the 1214 boolean "true", and the instance is the JSON primitive value 1215 "null", then the schema accepts the instance. Otherwise: 1217 o Let _E_ be the value of the schema member with the name "enum". 1218 The instance is accepted if and only if it is equal to one of the 1219 elements of _E_. 1221 If the instance is not accepted, then the error indicator for this 1222 case shall have an "instancePath" pointing to the instance, and a 1223 "schemaPath" pointing to the schema member with the name "enum". 1225 For example, the schema: 1227 { "enum": ["PENDING", "DONE", "CANCELED"] } 1229 Accepts 1231 "PENDING" 1233 and 1235 "DONE" 1237 and 1239 "CANCELED" 1241 but rejects all of 1243 0 1245 and 1247 1 1249 and 1251 2 1253 and 1255 "UNKNOWN" 1257 and 1259 null 1261 with the error indicator: 1263 [{ "instancePath": "", "schemaPath": "/enum" }] 1265 The schema 1267 { "enum": ["PENDING", "DONE", "CANCELED"], "nullable": true } 1269 accepts 1271 "PENDING" 1273 and 1275 null 1277 but rejects 1279 1 1281 and 1283 "UNKNOWN" 1285 with the error indicator: 1287 [{ "instancePath": "", "schemaPath": "/enum" }] 1289 3.3.5. Elements 1291 The "elements" form is meant to describe instances that must be 1292 arrays. A further sub-schema describes the elements of the array. 1293 The syntax of the "elements" form is described in Section 2.2.5. 1295 If a schema is of the elements form, then: 1297 o If the schema has a member named "nullable" whose value is the 1298 boolean "true", and the instance is the JSON primitive value 1299 "null", then the schema accepts the instance. Otherwise: 1301 o Let _S_ be the value of the schema member with the name 1302 "elements". The instance is accepted if and only if all of the 1303 following are true: 1305 * The instance is an array. Otherwise, the error indicator for 1306 this case shall have an "instancePath" pointing to the 1307 instance, and a "schemaPath" pointing to the schema member with 1308 the name "elements". 1310 * If the instance is an array, then every element of the instance 1311 must be accepted by _S_. Otherwise, the error indicators for 1312 this case are the union of all the errors arising from 1313 evaluating _S_ against elements of the instance. 1315 For example, the schema: 1317 { 1318 "elements": { 1319 "type": "float32" 1320 } 1321 } 1323 accepts 1325 [] 1327 and 1329 [1, 2, 3] 1331 but rejects 1333 null 1335 with the error indicator: 1337 [{ "instancePath": "", "schemaPath": "/elements" }] 1339 and rejects 1341 [1, 2, "foo", 3, "bar"] 1343 with the error indicators: 1345 [ 1346 { "instancePath": "/2", "schemaPath": "/elements/type" }, 1347 { "instancePath": "/4", "schemaPath": "/elements/type" } 1348 ] 1350 The schema 1352 { 1353 "elements": { 1354 "type": "float32" 1355 }, 1356 "nullable": true 1357 } 1359 accepts 1361 null 1363 and 1365 [] 1367 and 1369 [1, 2, 3] 1371 but rejects 1373 [1, 2, "foo", 3, "bar"] 1375 with the error indicators: 1377 [ 1378 { "instancePath": "/2", "schemaPath": "/elements/type" }, 1379 { "instancePath": "/4", "schemaPath": "/elements/type" } 1380 ] 1382 3.3.6. Properties 1384 The "properties" form is meant to describe JSON objects being used as 1385 a "struct". The syntax of the "properties" form is described in 1386 Section 2.2.6. 1388 If a schema is of the properties form, then: 1390 o If the schema has a member named "nullable" whose value is the 1391 boolean "true", and the instance is the JSON primitive value 1392 "null", then the schema accepts the instance. Otherwise the 1393 instance is accepted if and only if all of the following are true: 1395 o The instance is an object. 1397 Otherwise, the error indicator for this case shall have an 1398 "instancePath" pointing to the instance, and a "schemaPath" 1399 pointing to the schema member with the name "properties" if such a 1400 schema member exists; if such a member doesn't exist, "schemaPath" 1401 shall point to the schema member with the name 1402 "optionalProperties". 1404 o If the instance is an object and the schema has a member named 1405 "properties", then let _P_ be the value of the schema member named 1406 "properties". _P_, by Section 2.2.6, must be an object. For every 1407 member name in _P_, a member of the same name in the instance must 1408 exist. 1410 Otherwise, the error indicator for this case shall have an 1411 "instancePath" pointing to the instance, and a "schemaPath" 1412 pointing to the member of _P_ failing the requirement just 1413 described. 1415 o If the instance is an object, then let _P_ be the value of the 1416 schema member named "properties" (if it exists), and _O_ be the 1417 value of the schema member named "optionalProperties" (if it 1418 exists). 1420 For every member _I_ of the instance, find a member with the same 1421 name as _I_'s in _P_ or _O_. By Section 2.2.6, it is not possible 1422 for both _P_ and _O_ to have such a member. If the "discriminator 1423 tag exemption" is in effect on _I_ (see Section 3.3.8), then 1424 ignore _I_. Otherwise: 1426 * If no such member in _P_ or _O_ exists and validation is not in 1427 "allow additional properties" mode (see Section 3.1), then the 1428 instance is rejected. 1430 The error indicator for this case has an "instancePath" 1431 pointing to _I_, and a "schemaPath" pointing to the schema. 1433 * If such a member in _P_ or _O_ does exist, then call this 1434 member _S_. If _S_ rejects _I_'s value, then the instance is 1435 rejected. 1437 The error indicators for this case are the union of the error 1438 indicators from evaluating _S_ against _I_'s value. 1440 An instance may have multiple errors arising from the third and 1441 fourth bullet in the above. In this case, the error indicators are 1442 the union of the errors. 1444 For example, the schema: 1446 { 1447 "properties": { 1448 "a": { "type": "string" }, 1449 "b": { "type": "string" } 1450 }, 1451 "optionalProperties": { 1452 "c": { "type": "string" }, 1453 "d": { "type": "string" } 1454 } 1455 } 1457 accepts 1459 { "a": "foo", "b": "bar" } 1461 and 1463 { "a": "foo", "b": "bar", "c": "baz" } 1465 and 1467 { "a": "foo", "b": "bar", "c": "baz", "d": "quux" } 1469 and 1471 { "a": "foo", "b": "bar", "d": "quux" } 1473 but rejects 1475 null 1477 with the error indicator 1479 [{ "instancePath": "", "schemaPath": "/properties" }] 1481 and rejects 1483 { "b": 3, "c": 3, "e": 3 } 1485 with the error indicators 1487 [ 1488 { "instancePath": "", 1489 "schemaPath": "/properties/a" }, 1490 { "instancePath": "/b", 1491 "schemaPath": "/properties/b/type" }, 1492 { "instancePath": "/c", 1493 "schemaPath": "/optionalProperties/c/type" }, 1494 { "instancePath": "/e", 1495 "schemaPath": "" } 1496 ] 1498 If instead the schema had "additionalProperties: true", but was 1499 otherwise the same: 1501 { 1502 "properties": { 1503 "a": { "type": "string" }, 1504 "b": { "type": "string" } 1505 }, 1506 "optionalProperties": { 1507 "c": { "type": "string" }, 1508 "d": { "type": "string" } 1509 }, 1510 "additionalProperties": true 1511 } 1513 And the instance remained the same: 1515 { "b": 3, "c": 3, "e": 3 } 1517 Then the error indicators from evaluating the instance against the 1518 schema would be: 1520 [ 1521 { "instancePath": "", 1522 "schemaPath": "/properties/a" }, 1523 { "instancePath": "/b", 1524 "schemaPath": "/properties/b/type" }, 1525 { "instancePath": "/c", 1526 "schemaPath": "/optionalProperties/c/type" }, 1527 ] 1529 These are the same errors as before, except the final error 1530 (associated with the additional member named "e" in the instance) is 1531 no longer present. This is because "additionalProperties: true" 1532 enables "allow additional properties" mode on the schema. 1534 Finally, the schema: 1536 { 1537 "nullable": true, 1538 "properties": { 1539 "a": { "type": "string" }, 1540 "b": { "type": "string" } 1541 }, 1542 "optionalProperties": { 1543 "c": { "type": "string" }, 1544 "d": { "type": "string" } 1545 }, 1546 "additionalProperties": true 1547 } 1549 accepts 1551 null 1553 but rejects 1555 { "b": 3, "c": 3, "e": 3 } 1557 with the error indicators 1559 [ 1560 { "instancePath": "", 1561 "schemaPath": "/properties/a" }, 1562 { "instancePath": "/b", 1563 "schemaPath": "/properties/b/type" }, 1564 { "instancePath": "/c", 1565 "schemaPath": "/optionalProperties/c/type" }, 1566 ] 1568 3.3.7. Values 1570 The "values" form is meant to describe instances that are JSON 1571 objects being used as an associative array. The syntax of the 1572 "values" form is described in Section 2.2.7. 1574 If a schema is of the values form, then: 1576 o If the schema has a member named "nullable" whose value is the 1577 boolean "true", and the instance is the JSON primitive value 1578 "null", then the schema accepts the instance. Otherwise: 1580 o Let _S_ be the value of the schema member with the name "values". 1581 The instance is accepted if and only if all of the following are 1582 true: 1584 * The instance is an object. Otherwise, the error indicator for 1585 this case shall have an "instancePath" pointing to the 1586 instance, and a "schemaPath" pointing to the schema member with 1587 the name "values". 1589 * If the instance is an object, then every member value of the 1590 instance must be accepted by _S_. Otherwise, the error 1591 indicators for this case are the union of all the error 1592 indicators arising from evaluating _S_ against member values of 1593 the instance. 1595 For example, the schema: 1597 { 1598 "values": { 1599 "type": "float32" 1600 } 1601 } 1603 accepts 1605 {} 1607 and 1609 {"a": 1, "b": 2} 1611 but rejects 1613 null 1615 with the error indicator 1617 [{ "instancePath": "", "schemaPath": "/values" }] 1619 and rejects 1621 { "a": 1, "b": 2, "c": "foo", "d": 3, "e": "bar" } 1623 with the error indicators 1625 [ 1626 { "instancePath": "/c", "schemaPath": "/values/type" }, 1627 { "instancePath": "/e", "schemaPath": "/values/type" } 1628 ] 1630 The schema: 1632 { 1633 "nullable": true, 1634 "values": { 1635 "type": "float32" 1636 } 1637 } 1639 accepts 1641 null 1643 but rejects 1645 { "a": 1, "b": 2, "c": "foo", "d": 3, "e": "bar" } 1647 with the error indicators 1649 [ 1650 { "instancePath": "/c", "schemaPath": "/values/type" }, 1651 { "instancePath": "/e", "schemaPath": "/values/type" } 1652 ] 1654 3.3.8. Discriminator 1656 The "discriminator" form is meant to describe JSON objects being used 1657 in a fashion similar to a discriminated union construct in C-like 1658 languages. The syntax of the "discriminator" form is described in 1659 Section 2.2.8. 1661 When a schema is of the "discriminator" form, it validates: 1663 o That the instance is an object, 1665 o That the instance has a particular "tag" property, 1667 o That this "tag" property's value is a string within a set of valid 1668 values, and 1670 o That the instance satisfies another schema, where this other 1671 schema is chosen based on the value of the "tag" property. 1673 The behavior of the discriminator form is more complex than the other 1674 keywords. Readers familiar with CDDL may find the final example in 1675 Appendix B helpful in understanding its behavior. What follows in 1676 this section is a description of the discriminator form's behavior, 1677 as well as some examples. 1679 If a schema is of the "discriminator" form, then: 1681 o Let _D_ be the schema member with the name "discriminator". 1683 o Let _M_ be the schema member with the name "mapping". 1685 o Let _I_ be the instance member whose name equals _D_'s value. _I_ 1686 may, for some rejected instances, not exist. 1688 o Let _S_ be the member of _M_ whose name equals _I_'s value. _S_ 1689 may, for some rejected instances, not exist. 1691 If the schema has a member named "nullable" whose value is the 1692 boolean "true", and the instance is the JSON primitive value "null", 1693 then the schema accepts the instance. Otherwise the instance is 1694 accepted if and only if all of the following are true: 1696 o The instance is an object. 1698 Otherwise, the error indicator for this case shall have an 1699 "instancePath" pointing to the instance, and a "schemaPath" 1700 pointing to _D_. 1702 o If the instance is a JSON object, then _I_ must exist. 1704 Otherwise, the error indicator for this case shall have an 1705 "instancePath" pointing to the instance, and a "schemaPath" 1706 pointing to _D_. 1708 o If the instance is a JSON object and _I_ exists, _I_'s value must 1709 be a string. 1711 Otherwise, the error indicator for this case shall have an 1712 "instancePath" pointing to _I_, and a "schemaPath" pointing to 1713 _D_. 1715 o If the instance is a JSON object and _I_ exists and has a string 1716 value, then _S_ must exist. 1718 Otherwise, the error indicator for this case shall have an 1719 "instancePath" pointing to _I_, and a "schemaPath" pointing to 1720 _M_. 1722 o If the instance is a JSON object, _I_ exists, and _S_ exists, then 1723 the instance must satisfy _S_'s value. By Section 2, _S_'s value 1724 must be a schema of the properties form. Apply the "discriminator 1725 tag exemption" afforded in Section 3.3.6 to _I_ when evaluating 1726 whether the instance satisfies _S_'s value. 1728 Otherwise, the error indicators for this case shall be error 1729 indicators from evaluating _S_'s value against the instance, with 1730 the "discriminator tag exemption" applied to _I_. 1732 The list items above are defined in a mutually exclusive way. For 1733 any given instance and schema, exactly one of the list items above 1734 will apply. 1736 For example, the schema: 1738 { 1739 "discriminator": "version", 1740 "mapping": { 1741 "v1": { 1742 "properties": { 1743 "a": { "type": "float32" } 1744 } 1745 }, 1746 "v2": { 1747 "properties": { 1748 "a": { "type": "string" } 1749 } 1750 } 1751 } 1752 } 1754 rejects 1756 null 1758 with the error indicator 1760 [{ "instancePath": "", "schemaPath": "/discriminator" }] 1762 (This is the case of the instance not being an object.) 1764 Also rejected is 1766 {} 1768 with the error indicator 1770 [{ "instancePath": "", "schemaPath": "/discriminator" }] 1772 (This is the case of _I_ not existing.) 1774 Also rejected is 1775 { "version": 1 } 1777 with the error indicator 1779 [ 1780 { 1781 "instancePath": "/version", 1782 "schemaPath": "/discriminator" 1783 } 1784 ] 1786 (This is the case of _I_ existing, but not having a string value.) 1788 Also rejected is 1790 { "version": "v3" } 1792 with the error indicator 1794 [ 1795 { 1796 "instancePath": "/version", 1797 "schemaPath": "/mapping" 1798 } 1799 ] 1801 (This is the case of _I_ existing and having a string value, but _S_ 1802 not existing.) 1804 Also rejected is 1806 { "version": "v2", "a": 3 } 1808 with the error indicator 1810 [ 1811 { 1812 "instancePath": "/a", 1813 "schemaPath": "/mapping/v2/properties/a/type" 1814 } 1815 ] 1817 (This is the case of _I_ and _S_ existing, but the instance not 1818 satisfying _S_'s value.) 1820 Finally, the schema accepts 1822 { "version": "v2", "a": "foo" } 1824 This instance is accepted even though "version" is not mentioned by 1825 "/mapping/v2/properties"; the "discriminator tag exemption" ensures 1826 that "version" is not treated as an additional property when 1827 evaluating the instance against _S_'s value. 1829 By contrast, consider the same schema, but with "nullable" being 1830 "true". The schema: 1832 { 1833 "nullable": true, 1834 "discriminator": "version", 1835 "mapping": { 1836 "v1": { 1837 "properties": { 1838 "a": { "type": "float32" } 1839 } 1840 }, 1841 "v2": { 1842 "properties": { 1843 "a": { "type": "string" } 1844 } 1845 } 1846 } 1847 } 1849 accepts 1851 null 1853 To further illustrate the discriminator form with examples, recall 1854 the JTD schema in Section 2.2.8, reproduced here: 1856 { 1857 "discriminator": "event_type", 1858 "mapping": { 1859 "account_deleted": { 1860 "properties": { 1861 "account_id": { "type": "string" } 1862 } 1863 }, 1864 "account_payment_plan_changed": { 1865 "properties": { 1866 "account_id": { "type": "string" }, 1867 "payment_plan": { "enum": ["FREE", "PAID"] } 1868 }, 1869 "optionalProperties": { 1870 "upgraded_by": { "type": "string" } 1871 } 1872 } 1873 } 1874 } 1876 This schema accepts 1878 { "event_type": "account_deleted", "account_id": "abc-123" } 1880 and 1882 { 1883 "event_type": "account_payment_plan_changed", 1884 "account_id": "abc-123", 1885 "payment_plan": "PAID" 1886 } 1888 and 1890 { 1891 "event_type": "account_payment_plan_changed", 1892 "account_id": "abc-123", 1893 "payment_plan": "PAID", 1894 "upgraded_by": "users/mkhwarizmi" 1895 } 1897 but rejects 1899 {} 1901 with the error indicator 1903 [{ "instancePath": "", "schemaPath": "/discriminator" }] 1905 and rejects 1907 { "event_type": "some_other_event_type" } 1909 with the error indicator 1911 [ 1912 { 1913 "instancePath": "/event_type", 1914 "schemaPath": "/mapping" 1915 } 1916 ] 1918 and rejects 1920 { "event_type": "account_deleted" } 1922 with the error indicator 1924 [{ 1925 "instancePath": "", 1926 "schemaPath": "/mapping/account_deleted/properties/account_id" 1927 }] 1929 and rejects 1931 { 1932 "event_type": "account_payment_plan_changed", 1933 "account_id": "abc-123", 1934 "payment_plan": "PAID", 1935 "xxx": "asdf" 1936 } 1938 with the error indicator 1940 [{ 1941 "instancePath": "/xxx", 1942 "schemaPath": "/mapping/account_payment_plan_changed" 1943 }] 1945 4. IANA Considerations 1947 No IANA considerations. 1949 5. Security Considerations 1951 Implementations of JTD will necessarily be manipulating JSON data. 1952 Therefore, the security considerations of [RFC8259] are all relevant 1953 here. 1955 Implementations which evaluate user-inputted schemas SHOULD implement 1956 mechanisms to detect, and abort, circular references which might 1957 cause a naive implementation to go into an infinite loop. Without 1958 such mechanisms, implementations may be vulnerable to denial-of- 1959 service attacks. 1961 6. References 1963 6.1. Normative References 1965 [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate 1966 Requirement Levels", BCP 14, RFC 2119, 1967 DOI 10.17487/RFC2119, March 1997, 1968 . 1970 [RFC3339] Klyne, G. and C. Newman, "Date and Time on the Internet: 1971 Timestamps", RFC 3339, DOI 10.17487/RFC3339, July 2002, 1972 . 1974 [RFC4287] Nottingham, M., Ed. and R. Sayre, Ed., "The Atom 1975 Syndication Format", RFC 4287, DOI 10.17487/RFC4287, 1976 December 2005, . 1978 [RFC6901] Bryan, P., Ed., Zyp, K., and M. Nottingham, Ed., 1979 "JavaScript Object Notation (JSON) Pointer", RFC 6901, 1980 DOI 10.17487/RFC6901, April 2013, 1981 . 1983 [RFC8174] Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC 1984 2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174, 1985 May 2017, . 1987 [RFC8259] Bray, T., Ed., "The JavaScript Object Notation (JSON) Data 1988 Interchange Format", STD 90, RFC 8259, 1989 DOI 10.17487/RFC8259, December 2017, 1990 . 1992 [RFC8610] Birkholz, H., Vigano, C., and C. Bormann, "Concise Data 1993 Definition Language (CDDL): A Notational Convention to 1994 Express Concise Binary Object Representation (CBOR) and 1995 JSON Data Structures", RFC 8610, DOI 10.17487/RFC8610, 1996 June 2019, . 1998 6.2. Informative References 2000 [I-D.handrews-json-schema] 2001 Wright, A., Andrews, H., Hutton, B., and G. Dennis, "JSON 2002 Schema: A Media Type for Describing JSON Documents", 2003 draft-handrews-json-schema-02 (work in progress), 2004 September 2019. 2006 [OPENAPI] OpenAPI Initiative, "OpenAPI Specification", October 2019, 2007 . 2009 [RFC7071] Borenstein, N. and M. Kucherawy, "A Media Type for 2010 Reputation Interchange", RFC 7071, DOI 10.17487/RFC7071, 2011 November 2013, . 2013 [RFC7493] Bray, T., Ed., "The I-JSON Message Format", RFC 7493, 2014 DOI 10.17487/RFC7493, March 2015, 2015 . 2017 Appendix A. Rationale for Omitted Features 2019 This appendix is not normative. 2021 This section describes possible features which are intentionally left 2022 out of JSON Type Definition, and justifies why these features are 2023 omitted. 2025 A.1. Support for 64-bit Numbers 2027 This document does not allow "int64" or "uint64" as values for the 2028 JTD "type" keyword (see Section 2.2.3 and Section 3.3.3). Such 2029 hypothetical "int64" or "uint64" types would behave like "int32" or 2030 "uint32" (respectively), but with the range of values associated with 2031 64-bit instead of 32-bit integers, that is: 2033 o "int64" would accept numbers between -(2**63) and (2**63)-1 2035 o "uint64" would accept numbers between 0 and (2**64)-1 2037 Users of "int64" and "uint64" would likely expect that the full range 2038 of signed or unsigned 64-bit integers could interoperably be 2039 transmitted as JSON without loss of precision. But this assumption 2040 is likely to be incorrect, for the reasons given in Section 2.2 of 2041 [RFC7493]. 2043 "int64" and "uint64" likely would have led users to falsely assume 2044 that the full range of 64-bit integers can be interoperably processed 2045 as JSON without loss of precision. To avoid leading users astray, 2046 JTD omits "int64" and "uint64". 2048 A.2. Support for Non-Root Definitions 2050 This document disallows the "definitions" keyword from appearing 2051 outside of root schemas (see Figure 1). Conceivably, this document 2052 could have instead allowed "definitions" to appear on any schema, 2053 even non-root ones. Under this alternative design, "ref"s would 2054 resolve to a definition in the "nearest" (i.e., most nested) schema 2055 which both contained the "ref" and which had a suitably-named 2056 "definitions" member. 2058 For instance, under this alternative approach, one could define 2059 schemas like the one in Figure 3: 2061 { 2062 "properties": { 2063 "foo": { 2064 "definitions": { 2065 "user": { "properties": { "user_id": {"type": "string" }}} 2066 }, 2067 "ref": "user" 2068 }, 2069 "bar": { 2070 "definitions": { 2071 "user": { "properties": { "user_id": {"type": "string" }}} 2072 }, 2073 "ref": "user" 2074 }, 2075 "baz": { 2076 "definitions": { 2077 "user": { "properties": { "userId": {"type": "string" }}} 2078 }, 2079 "ref": "user" 2080 } 2081 } 2082 } 2084 Figure 3: A hypothetical schema had this document permitted non-root 2085 definitions. This is not a correct JTD schema. 2087 If schemas like that in Figure 3 were permitted, code generation from 2088 JTD schemas would be more difficult, and the generated code would be 2089 less useful. 2091 Code generation would be more difficult because it would force code 2092 generators to implement a name mangling scheme for types generated 2093 from definitions. This additional difficulty is not immense, but 2094 adds complexity to an otherwise relatively trivial task. 2096 Generated code would be less useful because generated, mangled struct 2097 names are less pithy than human-defined struct names. For instance, 2098 the "user" definitions in Figure 3 might have been generated into 2099 types named "PropertiesFooUser", "PropertiesBarUser", and 2100 "PropertiesBazUser"; obtuse names like these are less useful to 2101 human-written code than names like "User". 2103 Furthermore, even though "PropertiesFooUser" and "PropertiesBarUser" 2104 would be essentially identical, they would not be interchangeable in 2105 many statically-typed programming languages. A code generator could 2106 attempt to circumvent this by deduplicating identical definitions, 2107 but then the user might be confused as to why the subtly distinct 2108 "PropertiesBazUser", defined from a schema allowing a property named 2109 "userId" (not "user_id"), was not deduplicated. 2111 Because there seem to be implementation and usability challenges 2112 associated with non-root definitions, and because it would be easier 2113 to later amend JTD to permit for non-root definitions than to later 2114 amend JTD to prohibit them, this document does not permit non-root 2115 definitions in JTD schemas. 2117 Appendix B. Comparison with CDDL 2119 This appendix is not normative. 2121 To aid the reader familiar with CDDL, this section illustrates how 2122 JTD works by presenting JTD schemas and CDDL schemas which accept and 2123 reject the same instances. 2125 The JTD schema: 2127 {} 2129 accepts the same instances as the CDDL rule: 2131 root = any 2133 The JTD schema: 2135 { 2136 "definitions": { 2137 "a": { "elements": { "ref": "b" }}, 2138 "b": { "type": "float32" } 2139 }, 2140 "elements": { 2141 "ref": "a" 2142 } 2143 } 2145 accepts the same instances as the CDDL rule: 2147 root = [* a] 2149 a = [* b] 2150 b = number 2152 The JTD schema: 2154 { "enum": ["PENDING", "DONE", "CANCELED"]} 2156 accepts the same instances as the CDDL rule: 2158 root = "PENDING" / "DONE" / "CANCELED" 2160 The JTD schema: 2162 {"type": "boolean"} 2164 accepts the same instances as the CDDL rule: 2166 root = bool 2168 The JTD schemas: 2170 {"type": "float32"} 2172 and 2174 {"type": "float64"} 2176 both accept the same instances as the CDDL rule: 2178 root = number 2180 The JTD schema: 2182 {"type": "string"} 2184 accepts the same instances as the CDDL rule: 2186 root = tstr 2188 The JTD schema: 2190 {"type": "timestamp"} 2192 accepts the same instances as the CDDL rule: 2194 root = tdate 2196 The JTD schema: 2198 { "elements": { "type": "float32" }} 2200 accepts the same instances as the CDDL rule: 2202 root = [* number] 2204 The JTD schema: 2206 { 2207 "properties": { 2208 "a": { "type": "boolean" }, 2209 "b": { "type": "float32" } 2210 }, 2211 "optionalProperties": { 2212 "c": { "type": "string" }, 2213 "d": { "type": "timestamp" } 2214 } 2215 } 2217 accepts the same instances as the CDDL rule: 2219 root = { a: bool, b: number, ? c: tstr, ? d: tdate } 2221 The JTD schema: 2223 { "values": { "type": "float32" }} 2225 accepts the same instances as the CDDL rule: 2227 root = { * tstr => number } 2229 Finally, the JTD schema: 2231 { 2232 "discriminator": "a", 2233 "mapping": { 2234 "foo": { 2235 "properties": { 2236 "b": { "type": "float32" } 2237 } 2238 }, 2239 "bar": { 2240 "properties": { 2241 "b": { "type": "string" } 2242 } 2243 } 2244 } 2245 } 2247 accepts the same instances as the CDDL rule: 2249 root = { a: "foo", b: number } / { a: "bar", b: tstr } 2251 Appendix C. Example 2253 This appendix is not normative. 2255 As a demonstration of JTD, in Figure 4 is a JTD schema closely 2256 equivalent to the plain-English definition "reputation-object" 2257 described in Section 6.2.2 of [RFC7071]: 2259 { 2260 "properties": { 2261 "application": { "type": "string" }, 2262 "reputons": { 2263 "elements": { 2264 "additionalProperties": true, 2265 "properties": { 2266 "rater": { "type": "string" }, 2267 "assertion": { "type": "string" }, 2268 "rated": { "type": "string" }, 2269 "rating": { "type": "float32" }, 2270 }, 2271 "optionalProperties": { 2272 "confidence": { "type": "float32" }, 2273 "normal-rating": { "type": "float32" }, 2274 "sample-size": { "type": "float64" }, 2275 "generated": { "type": "float64" }, 2276 "expires": { "type": "float64" } 2277 } 2278 } 2279 } 2280 } 2281 } 2283 Figure 4: A JTD schema describing "reputation-object" from 2284 Section 6.6.2 of [RFC7071] 2286 This schema does not enforce the requirement that "sample-size", 2287 "generated", and "expires" be unbounded positive integers. It does 2288 not express the limitation that "rating", "confidence", and "normal- 2289 rating" should not have more than three decimal places of precision. 2291 The example in Figure 4 can be compared against the equivalent 2292 example in Appendix H of [RFC8610]. 2294 Acknowledgments 2296 Carsten Bormann provided lots of useful guidance and feedback on 2297 JTD's design and the structure of this document. 2299 Evgeny Poberezkin suggested the addition of "nullable", and 2300 thoroughly vetted this document for mistakes and opportunities for 2301 simplification. 2303 Tim Bray suggested the current "ref" model, and the addition of 2304 "enum". Anders Rundgren suggested extending "type" to have more 2305 support for numerical types. James Manger suggested additional 2306 clarifying examples of how integer types work. Adrian Farrel 2307 suggested many improvements to help make this document clearer. 2309 Members of the IETF JSON mailing list - in particular, Pete Cordell, 2310 Phillip Hallam-Baker, Nico Williams, John Cowan, Rob Sayre, and Erik 2311 Wilde - provided lots of useful feedback. 2313 OpenAPI's "discriminator" object [OPENAPI] inspired the 2314 "discriminator" form. [I-D.handrews-json-schema] influenced various 2315 parts of JTD's early design. 2317 Author's Address 2319 Ulysse Carion 2320 Segment.io, Inc 2321 100 California Street 2322 San Francisco 94111 2323 United States of America 2325 Email: ulysse@segment.com