idnits 2.17.1 draft-ucarion-jddf-05.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 (January 23, 2020) is 1553 days in the past. Is this intentional? Checking references for intended status: Experimental ---------------------------------------------------------------------------- -- Looks like a reference, but probably isn't: '1' on line 1124 -- Looks like a reference, but probably isn't: '2' on line 1124 -- Looks like a reference, but probably isn't: '3' on line 1124 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 January 23, 2020 5 Expires: July 26, 2020 7 JSON Data Definition Format (JDDF) 8 draft-ucarion-jddf-05 10 Abstract 12 This document proposes a format, called JSON Data Definition Format 13 (JDDF), 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, JDDF is strategically limited to be no more expressive 17 than the type systems of mainstream programming languages. This 18 strategic limitation, as well as the decision to make JDDF schemas be 19 JSON documents, also makes tooling atop of JDDF easier to build. 21 This document does not have IETF consensus and is presented here to 22 facilitate experimentation with the concept of JDDF. 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 July 26, 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 . . . . . . . . . . . . . . . . . . . . . . . . 2 59 1.1. Terminology . . . . . . . . . . . . . . . . . . . . . . . 5 60 1.2. Scope of Experiment . . . . . . . . . . . . . . . . . . . 5 61 2. Syntax . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 62 2.1. Extending JDDF's Syntax . . . . . . . . . . . . . . . . . 15 63 3. Semantics . . . . . . . . . . . . . . . . . . . . . . . . . . 16 64 3.1. Allowing Additional Properties . . . . . . . . . . . . . 16 65 3.2. Errors . . . . . . . . . . . . . . . . . . . . . . . . . 17 66 3.3. Forms . . . . . . . . . . . . . . . . . . . . . . . . . . 18 67 3.3.1. Empty . . . . . . . . . . . . . . . . . . . . . . . . 18 68 3.3.2. Ref . . . . . . . . . . . . . . . . . . . . . . . . . 18 69 3.3.3. Type . . . . . . . . . . . . . . . . . . . . . . . . 20 70 3.3.4. Enum . . . . . . . . . . . . . . . . . . . . . . . . 24 71 3.3.5. Elements . . . . . . . . . . . . . . . . . . . . . . 25 72 3.3.6. Properties . . . . . . . . . . . . . . . . . . . . . 26 73 3.3.7. Values . . . . . . . . . . . . . . . . . . . . . . . 29 74 3.3.8. Discriminator . . . . . . . . . . . . . . . . . . . . 30 75 4. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 36 76 5. Security Considerations . . . . . . . . . . . . . . . . . . . 36 77 6. References . . . . . . . . . . . . . . . . . . . . . . . . . 36 78 6.1. Normative References . . . . . . . . . . . . . . . . . . 36 79 6.2. Informative References . . . . . . . . . . . . . . . . . 37 80 Appendix A. Other Considerations . . . . . . . . . . . . . . . . 37 81 A.1. Support for 64-bit Numbers . . . . . . . . . . . . . . . 37 82 A.2. Support for Non-Root Schemas . . . . . . . . . . . . . . 38 83 Appendix B. Comparison with CDDL . . . . . . . . . . . . . . . . 40 84 Appendix C. Examples . . . . . . . . . . . . . . . . . . . . . . 43 85 Acknowledgments . . . . . . . . . . . . . . . . . . . . . . . . . 43 86 Author's Address . . . . . . . . . . . . . . . . . . . . . . . . 44 88 1. Introduction 90 This document describes a schema language for JSON [RFC8259] called 91 JSON Data Definition Format (JDDF). The name JDDF is chosen to avoid 92 confusion with "JSON Schema" from [I-D.handrews-json-schema]. 94 There exist many options for describing JSON data. JDDF's niche is 95 to focus on enabling code generation from schemas; to this end, 96 JDDF's expressiveness is strategically limited to be no more powerful 97 than what can be expressed in the type systems of mainstream 98 programming languages. 100 The goals of JDDF are to: 102 o Provide an unambiguous description of the overall structure of a 103 JSON document. 105 o Be able to describe common JSON datatypes and structures. That 106 is, the datatypes and structures necessary to support most JSON 107 documents, and which are widely understood in an interoperable way 108 by JSON implementations. 110 o Provide a single format that is readable and editable by both 111 humans and machines, and which can be embedded within other JSON 112 documents. This makes JDDF a convenient format for tooling to 113 accept as input, or produce as output. 115 o Enable code generation from JDDF schemas. JDDF schemas are meant 116 to be easy to convert into data structures idiomatic to a given 117 mainstream programming language. 119 o Provide a standardized format for errors when data does not 120 conform with a schema. 122 JDDF is intentionally designed as a rather minimal schema language. 123 Thus, although JDDF can describe JSON, it is not able to describe its 124 own structure: the Concise Data Definition Language (CDDL) [RFC8610] 125 is used to describe JDDF in this document. By keeping the 126 expressiveness of the schema language minimal, JDDF makes code 127 generation and standardized errors easier to implement. 129 Examples in this document use constructs from the C++ programming 130 language. These examples are provided to aid the reader in 131 understanding the principles of JDDF, but are not limiting in any 132 way. 134 JDDF's feature set is designed to represent common patterns in JSON- 135 using applications, while still having a clear correspondence to 136 programming languages in widespread use. Thus, JDDF supports: 138 o Signed and unsigned 8, 16, and 32-bit integers. A tool which 139 converts JDDF schemas into code can use "int8_t", "uint8_t", 140 "int16_t", etc., or their equivalents in the target language, to 141 represent these JDDF types. 143 o A distinction between "float32" and "float64". Code generators 144 can use "float" and "double", or their equivalents, for these JDDF 145 types. 147 o A "properties" form of JSON objects, corresponding to some sort of 148 struct or record. The "properties" form of JSON objects is akin 149 to a C++ "struct". 151 o A "values" form of JSON objects, corresponding to some sort of 152 dictionary or associative array. The "values" form of JSON 153 objects is akin to a C++ "std::map". 155 o A "discriminator" form of JSON objects, corresponding to a 156 discriminated (or "tagged") union. The "discriminator" form of 157 JSON objects is akin to a C++ "std::variant". 159 The principle of common patterns in JSON is why JDDF does not support 160 64-bit integers, as these are usually transmitted over JSON in a non- 161 interoperable (i.e., ignoring the recommendations in Section 2.2 of 162 [RFC7493]) or mutually inconsistent (e.g., using hexadecimal versus 163 base64) ways. Appendix A.1 further elaborates on why JDDF does not 164 support 64-bit integers. 166 The principle of clear correspondence to common programming languages 167 is why JDDF does not support, for example, a data type for numbers up 168 to 2**53-1. 170 It is expected that for many use-cases, a schema language of JDDF's 171 expressiveness is sufficient. Where a more expressive language is 172 required, alternatives exist in CDDL and others. 174 This document does not have IETF consensus and is presented here to 175 facilitate experimentation with the concept of JDDF. The purpose of 176 the experiment is to gain experience with JDDF and to possibly revise 177 this work accordingly. If JDDF is determined to be a valuable and 178 popular approach it may be taken to the IETF for further discussion 179 and revision. 181 This document has the following structure: 183 The syntax of JDDF is defined in Section 2. Section 3 describes the 184 semantics of JDDF; this includes determining whether some data 185 satisfies a schema and what error indicators should be produced when 186 the data is unsatisfactory. Appendix A discusses why certain 187 features are omitted from JDDF. Appendix B presents various JDDF 188 schemas and their CDDL equivalents. 190 1.1. Terminology 192 The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", 193 "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and 194 "OPTIONAL" in this document are to be interpreted as described in 195 BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all 196 capitals, as shown here. These words may also appear in this 197 document in lower case as plain English words, absent their normative 198 meanings. 200 The term "JSON Pointer", when it appears in this document, is to be 201 understood as it is defined in [RFC6901]. 203 The terms "object", "member", "array", "number", "name", and "string" 204 in this document are to be interpreted as described in [RFC8259]. 206 The term "instance", when it appears in this document, refers to a 207 JSON value being validated against a JDDF schema. 209 1.2. Scope of Experiment 211 JDDF is an experiment. Participation in this experiment consists of 212 using JDDF to validate or document interchanged JSON messages, or in 213 building tooling atop of JDDF. Feedback on the results of this 214 experiment may be e-mailed to the author. Participants in this 215 experiment are anticipated to mostly be nodes which provide or 216 consume JSON-based APIs. 218 Nodes know if they are participating in the experiment if they are 219 validating JSON messages against a JDDF schema, or if they are 220 relying on another node to do so. Nodes are also participating in 221 the experiment if they are running code generated from a JDDF schema. 223 The risk of this experiment "escaping" takes the form of a JDDF- 224 supporting node expecting another node, which lacks such support, to 225 validate messages against some JDDF schema. In such a case, the 226 outcome will likely be that the nodes fail to interchange information 227 correctly. 229 This experiment will be deemed successful when JDDF has been 230 implemented by multiple independent parties, and these parties 231 successfully use JDDF to facilitate information interchange within 232 their internal systems or between systems operated by independent 233 parties. 235 If this experiment is deemed successful, and JDDF is determined to be 236 a valuable and popular approach, it may be taken to the IETF for 237 further discussion and revision. One possible outcome of this 238 discussion and revision could be that a working group produces a 239 Standards Track specification of JDDF. 241 Some implementations of JDDF, as well as code generators and other 242 tooling related to JDDF, are available at . 244 2. Syntax 246 This section describes when a JSON document is a correct JDDF schema. 247 Because CDDL is well-suited to the task of defining complex JSON 248 formats, such as JDDF schemas, this section uses CDDL to describe the 249 format of JDDF schemas. 251 JDDF schemas may recursively contain other schemas. In this 252 document, a "root schema" is one which is not contained within 253 another schema, i.e. it is "top level". 255 A JDDF schema is a JSON object taking on an appropriate form. JDDF 256 schemas may contain "additional data", discussed in Section 2.1. 257 Root JDDF schemas may optionally contain definitions (a mapping from 258 names to schemas). 260 A correct root JDDF schema MUST match the "root-schema" CDDL rule 261 described in this section. A correct non-root JDDF schema MUST match 262 the "schema" CDDL rule described in this section. 264 ; root-schema is identical to schema, but additionally allows for 265 ; definitions. 266 ; 267 ; definitions are prohibited from appearing on non-root schemas. 268 root-schema = { 269 schema, 270 ? definitions: { * tstr => schema }, 271 } 273 ; schema is the main CDDL rule defining a JDDF schema. Certain JDDF 274 ; schema forms will be defined recursively in terms of this rule. 275 schema = { 276 form, 277 * non-keyword => * 278 } 280 ; non-keyword is constructed here so as to prevent it from matching 281 ; any of the keywords defined later. 282 non-keyword = 283 (((((((((.ne "definitions") 284 .ne "ref") 285 .ne "type") 286 .ne "enum") 287 .ne "elements") 288 .ne "properties") 289 .ne "optionalProperties") 290 .ne "additionalProperties") 291 .ne "values") 292 .ne "discriminator" 294 Figure 1: CDDL definition of a schema 296 Thus Figure 2 is not a correct JDDF schema, as its "definitions" 297 object contains a number, which is not a schema: 299 { "definitions": { "foo": 3 }} 301 Figure 2: An incorrect JDDF schema. JSON numbers are not JDDF 302 schemas 304 Figure 3 is also incorrect, as a "definitions" object may not appear 305 on non-root schemas. See Figure 16 for more details on how 306 "elements" is defined in terms of the "schema" CDDL rule. 308 { 309 "elements": { 310 "definitions": {} 311 } 312 } 314 Figure 3: An incorrect JDDF schema. "definitions" may appear only in 315 root schemas 317 Figure 4 is an example of a correct schema that uses "definitions": 319 { 320 "definitions": { 321 "user": { 322 "properties": { 323 "name": { "type": "string" }, 324 "create_time": { "type": "timestamp" } 325 } 326 } 327 }, 328 "elements": { 329 "ref": "user" 330 } 331 } 333 Figure 4: A correct JDDF schema using "definitions" 335 JDDF schemas can take on one of eight forms. These forms are defined 336 so as to be mutually exclusive; a schema cannot satisfy multiple 337 forms at once. 339 form = empty / 340 ref / 341 type / 342 enum / 343 elements / 344 properties / 345 values / 346 discriminator 348 Figure 5: CDDL definition of the JDDF schema forms 350 The first form, "empty", is trivial. It is meant for matching any 351 instance: 353 empty = {} 355 Figure 6: CDDL definition of the "empty" form 357 Thus, Figure 7 is a correct schema: 359 {} 361 Figure 7: A JDDF schema of the "empty" form 363 The empty form is not very useful by itself, and it meant to be used 364 as a sub-schema. Schema authors can use the empty form to describe 365 parts of a message format which do not contain predictable data, or 366 which the author does not want to specify. 368 The semantics of schemas of the empty form are described in 369 Section 3.3.1. 371 The second form, "ref", is for when a schema is defined in terms of 372 something in the "definitions" of the root schema: 374 ref = { ref: tstr } 376 Figure 8: CDDL definition of the "ref" form 378 For a schema to be correct, the "ref" value must refer to one of the 379 definitions found at the root level of the schema it appears in. 380 More formally, for a schema _S_ of the "ref" form: 382 o Let _B_ be the root schema containing the schema, or the schema 383 itself if it is a root schema. 385 o Let _R_ be the value of the member of _S_ with the name "ref". 387 If the schema is correct, then _B_ must have a member _D_ with the 388 name "definitions", and _D_ must contain a member whose name equals 389 _R_. 391 Figure 9 is a correct example of "ref" being used to avoid re- 392 defining the same thing twice: 394 { 395 "definitions": { 396 "coordinates": { 397 "properties": { 398 "lat": { "type": "float32" }, 399 "lng": { "type": "float32" } 400 } 401 } 402 }, 403 "properties": { 404 "user_location": { "ref": "coordinates" }, 405 "server_location": { "ref": "coordinates" } 406 } 407 } 409 Figure 9: A correct JDDF schema using the "ref" form 411 However, Figure 10 is incorrect, as it refers to a definition that 412 doesn't exist: 414 { 415 "definitions": { "foo": { "type": "float32" }}, 416 "ref": "bar" 417 } 419 Figure 10: An incorrect JDDF schema. There is no "bar" in 420 "definitions" 422 The semantics of schemas of the "ref" form are described in 423 Section 3.3.2. 425 The third form, "type", constrains instances to have a particular 426 primitive type. The precise meaning of each of the primitive types 427 is described in Section 3.3.3. 429 type = { type: "boolean" / num-type / "string" / "timestamp" } 430 num-type = "float32" / "float64" / 431 "int8" / "uint8" / "int16" / "uint16" / "int32" / "uint32" 433 Figure 11: CDDL Definition of the Type Form 435 For example, Figure 12 constrains instances to be strings that are 436 correct [RFC3339] timestamps: 438 { "type": "timestamp" } 440 Figure 12: A correct JDDF schema using the "type" form 442 The semantics of schemas of the "type" form are described in 443 Section 3.3.3. 445 The fourth form, "enum", describes instances whose value must be one 446 of a finite, predetermined set of values: 448 enum = { enum: [+ tstr] } 450 Figure 13: CDDL definition of the "enum" form 452 The values within "[+ tstr]" MUST NOT contain duplicates. Thus, 453 Figure 14 is a correct schema: 455 { "enum": ["IN_PROGRESS", "DONE", "CANCELED"] } 457 Figure 14: A correct JDDF schema using the "enum" form 459 But Figure 15 is not a correct schema, as "B" is duplicated: 461 { "enum": ["A", "B", "B"] } 463 Figure 15: An incorrect JDDF schema. "B" appears twice. 465 The semantics of schemas of the "enum" form are described in 466 Section 3.3.4. 468 The fifth form, "elements", describes instances that must be arrays. 469 A further sub-schema describes the elements of the array. 471 elements = { elements: schema } 473 Figure 16: CDDL definition of the "elements" form 475 Figure 17 is a schema describing an array of [RFC3339] timestamps: 477 { "elements": { "type": "timestamp" }} 479 Figure 17: A correct JDDF schema using the "elements" form 481 The semantics of schemas of the "elements" form are described in 482 Section 3.3.5. 484 The sixth form, "properties", describes JSON objects being used as a 485 "struct". A schema of this form specifies the names of required and 486 optional properties, as well as the schemas each of those properties 487 must satisfy: 489 ; One of properties or optionalProperties may be omitted, 490 ; but not both. 491 properties = with-properties / with-optional-properties 493 with-properties = { 494 properties: * tstr => schema, 495 ? optionalProperties * tstr => schema, 496 ? additionalProperties: bool, 497 } 499 with-optional-properties = { 500 ? properties: * tstr => schema, 501 optionalProperties: * tstr => schema, 502 ? additionalProperties: bool, 503 } 505 Figure 18: CDDL definition of the "properties" form 507 If a schema has both a member named "properties" (with value _P_) and 508 another member named "optionalProperties" (with value _O_), then _O_ 509 and _P_ MUST NOT have any member names in common. This is to prevent 510 ambiguity as to whether a property is optional or required. 512 Thus, Figure 19 is not a correct schema, as "confusing" appears in 513 both "properties" and "optionalProperties": 515 { 516 "properties": { "confusing": {} }, 517 "optionalProperties": { "confusing": {} } 518 } 520 Figure 19: An incorrect JDDF schema. "confusing" is repeated between 521 "properties" and "optionalProperties" 523 Figure 20 is a correct schema, describing a paginated list of users: 525 { 526 "properties": { 527 "users": { 528 "elements": { 529 "properties": { 530 "id": { "type": "string" }, 531 "name": { "type": "string" }, 532 "create_time": { "type": "timestamp" } 533 }, 534 "optionalProperties": { 535 "delete_time": { "type": "timestamp" } 536 } 537 } 538 }, 539 "next_page_token": { "type": "string" } 540 } 541 } 543 Figure 20: A correct JDDF schema using the "properties" form 545 The semantics of schemas of the "properties" form are described in 546 Section 3.3.6. 548 The seventh form, "values", describes JSON objects being used as an 549 associative array. A schema of this form specifies the form all 550 member values must satisfy, but places no constraints on the member 551 names: 553 values = { values: * tstr => schema } 555 Figure 21: CDDL definition of the "values" form 557 Thus, Figure 22 is a correct schema, describing a mapping from 558 strings to numbers: 560 { "values": { "type": "float32" }} 562 Figure 22: A correct JDDF schema using the "values 564 The semantics of schemas of the "values" form are described in 565 Section 3.3.7. 567 Finally, the eighth form, "discriminator", describes JSON objects 568 being used as a discriminated union. A schema of this form specifies 569 the "tag" (or "discriminator") of the union, as well as a mapping 570 from tag values to the appropriate schema to use. 572 ; Note well: the values of mapping are of the properties form. 573 discriminator = { tag: tstr, mapping: * tstr => properties } 575 Figure 23: CDDL definition of the "discriminator" form 577 To prevent ambiguous or unsatisfiable contstraints on the "tag" of a 578 discriminator, an additional constraint on schemas of the 579 discriminator form exists. For schemas of the discriminator form: 581 o Let _D_ be the schema member with the name "discriminator". 583 o Let _T_ be the member of _D_ with the name "tag". 585 o Let _M_ be the member of _D_ with the name "mapping". 587 If the schema is correct, then all member values _S_ of _M_ will be 588 schemas of the "properties" form. For each member _P_ of _S_ whose 589 name equals "properties" or "optionalProperties", _P_'s value, which 590 must be an object, MUST NOT contain any members whose name equals 591 _T_'s value. 593 Thus, Figure 24 is an incorrect schema, as "event_type" is both the 594 value of "tag" and a member name in one of the "mapping" member 595 "properties": 597 { 598 "tag": "event_type", 599 "mapping": { 600 "is_event_type_a_string_or_a_float32?": { 601 "properties": { "event_type": { "type": "float32" }} 602 } 603 } 604 } 606 Figure 24: An incorrect JDDF schema. "event_type" appears both in 607 "tag" and in the "properties" of a "mapping" value 609 However, Figure 25 is a correct schema, describing a pattern of data 610 common in JSON-based messaging systems: 612 { 613 "tag": "event_type", 614 "mapping": { 615 "account_deleted": { 616 "properties": { 617 "account_id": { "type": "string" } 618 } 619 }, 620 "account_payment_plan_changed": { 621 "properties": { 622 "account_id": { "type": "string" }, 623 "payment_plan": { "enum": ["FREE", "PAID"] } 624 }, 625 "optionalProperties": { 626 "upgraded_by": { "type": "string" } 627 } 628 } 629 } 630 } 632 Figure 25: A correct JDDF schema using the "discriminator" form 634 The semantics of schemas of the "discriminator" form are described in 635 Section 3.3.8. Section 3.3.8 also includes examples of what 636 Figure 25 accepts and rejects. 638 2.1. Extending JDDF's Syntax 640 This document does not describe any extension mechanisms for JDDF 641 schema validation, which is described in Section 3. However, schemas 642 (through the "non-keyword" CDDL rule in Section 2) are defined to 643 allow members whose names are not equal to any of the specially- 644 defined keywords (i.e. "definitions", "elements", etc.). Call these 645 members "non-keyword members". 647 Users MAY add additional, non-keyword members to JDDF schemas to 648 convey information that is not pertinent to validation. For example, 649 such non-keyword members could provide hints to code generators, or 650 trigger some special behavior for a library that generates user 651 interfaces from schemas. 653 Users SHOULD NOT expect non-keyword members to be understood by other 654 parties. As a result, if consistent validation with other parties is 655 a requirement, users SHOULD NOT use non-keyword members to affect how 656 schema validation, as described in Section 3, works. 658 Users MAY expect expect non-keywords to be understood by other 659 parties, and MAY use non-keyword members to affect how schema 660 validation works, if these other parties are somehow known to support 661 these non-keyword members. For example, two parties may agree, out 662 of band, that they will support an extended JDDF with a custom 663 keyword. 665 3. Semantics 667 This section describes when an instance is valid against a correct 668 JDDF schema, and the error indicators to produce when an instance is 669 invalid. 671 3.1. Allowing Additional Properties 673 Users will have different desired behavior with respect to 674 "unspcecified" members in an instance. For example, consider the 675 JDDF schema in Figure 26: 677 { "properties": { "a": { "type": "string" }}} 679 Figure 26: An illustrative JDDF schema 681 Some users may expect that 683 {"a": "foo", "b": "bar"} 685 satisfies the schema in Figure 26. Others may disagree, as "b" is 686 not one of the properties described in the schema. In this document, 687 allowing such "unspecified" members, like "b" in this example, 688 happens when evaluation is in "allow additional properties" mode. 690 Evaluation of a schema does not allow additional properties by 691 default, but can be overridden by having the schema include a member 692 named "additionalProperties", where that member has a value of 693 "true". 695 More formally: evaluation of a schema _S_ is in "allow additional 696 properties" mode if there exists a member of _S_ whose name equals 697 "additionalProperties", and whose value is a boolean "true". 698 Otherwise, evaluation of _S_ is not in "allow additional properties" 699 mode. 701 See Section 3.3.6 for how allowing unknown properties affects schema 702 evaluation, but briefly, consider the schema in Figure 27: 704 { "properties": { "a": { "type": "string" }}} 706 Figure 27: A JDDF schema that does not allow additional properties 708 The schema in Figure 27 rejects 710 {"a": "foo", "b": "bar"} 712 However, consider the schema in Figure 28: 714 { 715 "additionalProperties": true, 716 "properties": { "a": { "type": "string" }} 717 } 719 Figure 28: A JDDF schema that allows additional properties 721 The schema in Figure 28 accepts 723 {"a": "foo", "b": "bar"} 725 Note that "additionalProperties" does not get "inherited" by sub- 726 schemas. For example, the JDDF schema: 728 { 729 "additionalProperties": true, 730 "properties": { 731 "a": { 732 "properties": { 733 "b": { "type": "string" } 734 } 735 } 736 } 737 } 739 accepts 741 { "a": { "b": "c" }, "foo": "bar" } 743 but rejects 745 { "a": { "b": "c", "foo": "bar" }} 747 because the "additionalProperties" at the root level does not affect 748 the behavior of sub-schemas. 750 3.2. Errors 752 To facilitate consistent validation error handling, this document 753 specifies a standard error indicator format. Implementations SHOULD 754 support producing error indicators in this standard form. 756 The standard error indicator format is a JSON array. The order of 757 the elements of this array is not specified. The elements of this 758 array are JSON objects with the members: 760 o A member with the name "instancePath", whose value is a JSON 761 string encoding a JSON Pointer. This JSON Pointer will point to 762 the part of the instance that was rejected. 764 o A member with the name "schemaPath", whose value is a JSON string 765 encoding a JSON Pointer. This JSON Pointer will point to the part 766 of the schema that rejected the instance. 768 The values for "instancePath" and "schemaPath" depend on the form of 769 the schema, and are described in detail in Section 3.3. 771 3.3. Forms 773 This section describes, for each of the eight JDDF schema forms, the 774 rules dictating whether an instance is accepted, as well as the error 775 indicators to produce when an instance is invalid. 777 The forms a correct schema may take on are formally described in 778 Section 2. 780 3.3.1. Empty 782 The empty form is meant to describe instances whose values are 783 unknown, unpredictable, or otherwise unconstrained by the schema. 785 If a schema is of the empty form, then it accepts all instances. A 786 schema of the empty form will never produce any error indicators. 788 3.3.2. Ref 790 The ref form is for when a schema is defined in terms of something in 791 the "definitions" of the root schema. The ref form enables schemas 792 to be less repetitive, and also enables describing recursive 793 structures. 795 If a schema is of the ref form, then: 797 o Let _B_ be the root schema containing the schema, or the schema 798 itself if it is a root schema. 800 o Let _D_ be the member of _B_ with the name "definitions". By 801 Section 2, _D_ exists. 803 o Let _R_ be the value of the schema member with the name "ref". 805 o Let _S_ be the value of the member of _D_ whose name equals _R_. 806 By Section 2, _S_ exists, and is a schema. 808 The schema accepts the instance if and only if _S_ accepts the 809 instance. Otherwise, the error indicators to return in this case are 810 the union of the error indicators from evaluating _S_ against the 811 instance. 813 For example, the schema: 815 { 816 "definitions": { "a": { "type": "float32" }}, 817 "ref": "a" 818 } 820 Figure 29: A JDDF schema demonstrating the "ref" form 822 Accepts 824 123 826 but not 828 false 830 The error indicators to produce when evaluting 832 false 834 against the schema in Figure 29 are: 836 [{ "instancePath": "", "schemaPath": "/definitions/a/type" }] 838 Note that the ref form is defined to only look up definitions at the 839 root level. Thus, with the schema: 841 { 842 "definitions": { "a": { "type": "float32" }}, 843 "elements": { 844 "definitions": { "a": { "type": "boolean" }}, 845 "ref": "a" 846 } 847 } 849 The instance 851 123 853 is accepted, and 855 false 857 is rejected, and the error indicator would be: 859 [{ "instancePath": "", "schemaPath": "/definitions/a/type" }] 861 Though non-root definitions are not syntactically disallowed in 862 correct schemas, they are entirely immaterial to evaluating 863 references. 865 3.3.3. Type 867 The type form is meant to describe instances whose value is a 868 boolean, number, string, or timestamp ([RFC3339]). 870 If a schema is of the type form, then let _T_ be the value of the 871 member with the name "type". The following table describes whether 872 the instance is accepted, as a function of _T_'s value: 874 +-------------------+----------------------------------------------+ 875 | If _T_ equals ... | then the instance is accepted if it is ... | 876 +-------------------+----------------------------------------------+ 877 | boolean | equal to "true" or "false" | 878 | | | 879 | float32 | a JSON number | 880 | | | 881 | float64 | a JSON number | 882 | | | 883 | int8 | See Table 2 | 884 | | | 885 | uint8 | See Table 2 | 886 | | | 887 | int16 | See Table 2 | 888 | | | 889 | uint16 | See Table 2 | 890 | | | 891 | int32 | See Table 2 | 892 | | | 893 | uint32 | See Table 2 | 894 | | | 895 | string | a JSON string | 896 | | | 897 | timestamp | a JSON string encoding a [RFC3339] timestamp | 898 +-------------------+----------------------------------------------+ 900 Table 1: Accepted Values for Type 902 "float32" and "float64" are distinguished from each other in their 903 intent. "float32" indicates data intended to be processed as an IEEE 904 754 single-precision float, whereas "float64" indicates data intended 905 to be processed as an IEEE 754 double-precision float. Tools which 906 generate code from JDDF schemas will likely produce different code 907 for "float32" than for "float64". 909 If _T_ starts with "int" or "uint", then the instance is accepted if 910 and only if it is a JSON number encoding a value with zero fractional 911 part. Depending on the value of _T_, this encoded number must 912 additionally fall within a particular range: 914 +--------+---------------------------+---------------------------+ 915 | _T_ | Minimum Value (Inclusive) | Maximum Value (Inclusive) | 916 +--------+---------------------------+---------------------------+ 917 | int8 | -128 | 127 | 918 | | | | 919 | uint8 | 0 | 255 | 920 | | | | 921 | int16 | -32,768 | 32,767 | 922 | | | | 923 | uint16 | 0 | 65,535 | 924 | | | | 925 | int32 | -2,147,483,648 | 2,147,483,647 | 926 | | | | 927 | uint32 | 0 | 4,294,967,295 | 928 +--------+---------------------------+---------------------------+ 930 Table 2: Ranges for Integer Types 932 Note that 934 10 936 and 938 10.0 940 and 942 1.0e1 944 encode values with zero fractional part, whereas 946 10.5 948 encodes a number with a non-zero fractional part. Thus the schema 949 {"type": "int8"} 951 accepts 953 10 955 and 957 10.0 959 and 961 1.0e1 963 but rejects 965 10.5 967 as well as 969 false 971 because "false" is not a number at all. 973 If the instance is not accepted, then the error indicator for this 974 case shall have an "instancePath" pointing to the instance, and a 975 "schemaPath" pointing to the schema member with the name "type". 977 For example, the schema: 979 {"type": "boolean"} 981 accepts 983 false 985 but rejects 987 127 989 The schema: 991 {"type": "float32"} 993 accepts 995 10.5 997 and 999 127 1001 but rejects 1003 false 1005 The schema: 1007 {"type": "string"} 1009 accepts 1011 "1985-04-12T23:20:50.52Z" 1013 and 1015 "foo" 1017 but rejects 1019 false 1021 The schema: 1023 {"type": "timestamp"} 1025 accepts 1027 "1985-04-12T23:20:50.52Z" 1029 but rejects 1031 "foo" 1033 and 1035 false 1037 In all of the examples of rejected instances given in this section, 1038 the error indicator to produce is: 1040 [{ "instancePath": "", "schemaPath": "/type" }] 1042 3.3.4. Enum 1044 The enum form is meant to describe instances whose value must be one 1045 of a finite, predetermined set of string values. 1047 If a schema is of the enum form, then let _E_ be the value of the 1048 schema member with the name "enum". The instance is accepted if and 1049 only if it is equal to one of the elements of _E_. 1051 If the instance is not accepted, then the error indicator for this 1052 case shall have an "instancePath" pointing to the instance, and a 1053 "schemaPath" pointing to the schema member with the name "enum". 1055 For example, the schema: 1057 { "enum": ["PENDING", "DONE", "CANCELED"] } 1059 Accepts 1061 "PENDING" 1063 and 1065 "DONE" 1067 and 1069 "CANCELED" 1071 but rejects all of 1073 0 1075 and 1077 1 1079 and 1081 2 1083 and 1085 "UNKNOWN" 1087 with the error indicator: 1089 [{ "instancePath": "", "schemaPath": "/enum" }] 1091 3.3.5. Elements 1093 The elements form is meant to describe instances that must be arrays. 1094 A further sub-schema describes the elements of the array. 1096 If a schema is of the elements form, then let _S_ be the value of the 1097 schema member with the name "elements". The instance is accepted if 1098 and only if all of the following are true: 1100 o The instance is an array. Otherwise, the error indicator for this 1101 case shall have an "instancePath" pointing to the instance, and a 1102 "schemaPath" pointing to the schema member with the name 1103 "elements". 1105 o If the instance is an array, then every element of the instance 1106 must be accepted by _S_. Otherwise, the error indicators for this 1107 case are the union of all the errors arising from evaluating _S_ 1108 against elements of the instance. 1110 For example, the schema: 1112 { 1113 "elements": { 1114 "type": "float32" 1115 } 1116 } 1118 accepts 1120 [] 1122 and 1124 [1, 2, 3] 1126 but rejects 1128 false 1130 with the error indicator: 1132 [{ "instancePath": "", "schemaPath": "/elements" }] 1134 and rejects 1136 [1, 2, "foo", 3, "bar"] 1138 with the error indicators: 1140 [ 1141 { "instancePath": "/2", "schemaPath": "/elements/type" }, 1142 { "instancePath": "/4", "schemaPath": "/elements/type" } 1143 ] 1145 3.3.6. Properties 1147 The properties form is meant to describe JSON objects being used as a 1148 "struct". 1150 If a schema is of the properties form, then the instance is accepted 1151 if and only if all of the following are true: 1153 o The instance is an object. 1155 Otherwise, the error indicator for this case shall have an 1156 "instancePath" pointing to the instance, and a "schemaPath" 1157 pointing to the schema member with the name "properties" if such a 1158 schema member exists; if such a member doesn't exist, "schemaPath" 1159 shall point to the schema member with the name 1160 "optionalProperties". 1162 o If the instance is an object and the schema has a member named 1163 "properties", then let _P_ be the value of the schema member named 1164 "properties". _P_, by Section 2, must be an object. For every 1165 member name in _P_, a member of the same name in the instance must 1166 exist. 1168 Otherwise, the error indicator for this case shall have an 1169 "instancePath" pointing to the instance, and a "schemaPath" 1170 pointing to the member of _P_ failing the requirement just 1171 described. 1173 o If the instance is an object, then let _P_ be the value of the 1174 schema member named "properties" (if it exists), and _O_ be the 1175 value of the schema member named "optionalProperties" (if it 1176 exists). 1178 For every member _I_ of the instance, find a member with the same 1179 name as _I_'s in _P_ or _O_. By Section 2, it is not possible for 1180 both _P_ and _O_ to have such a member. If the "discriminator tag 1181 exemption" is in effect on _I_ (see Section 3.3.8), then ignore 1182 _I_. Otherwise: 1184 * If no such member in _P_ or _O_ exists and validation is not in 1185 "allow additional properties" mode (see Section 3.1), then the 1186 instance is rejected. 1188 The error indicator for this case has an "instancePath" 1189 pointing to _I_, and a "schemaPath" pointing to the schema. 1191 * If such a member in _P_ or _O_ does exist, then call this 1192 member _S_. If _S_ rejects _I_'s value, then the instance is 1193 rejected. 1195 The error indicators for this case are the union of the error 1196 indicators from evaluating _S_ against _I_'s value. 1198 An instance may have multiple errors arising from the second and 1199 third bullet in the above. In this case, the error indicators are 1200 the union of the errors. 1202 For example, the schema: 1204 { 1205 "properties": { 1206 "a": { "type": "string" }, 1207 "b": { "type": "string" } 1208 }, 1209 "optionalProperties": { 1210 "c": { "type": "string" }, 1211 "d": { "type": "string" } 1212 } 1213 } 1215 accepts 1217 { "a": "foo", "b": "bar" } 1219 and 1221 { "a": "foo", "b": "bar", "c": "baz" } 1223 and 1225 { "a": "foo", "b": "bar", "c": "baz", "d": "quux" } 1227 and 1229 { "a": "foo", "b": "bar", "d": "quux" } 1231 but rejects 1233 123 1235 with the error indicator 1237 [{ "instancePath": "", "schemaPath": "/properties" }] 1239 and rejects 1241 { "b": 3, "c": 3, "e": 3 } 1243 with the error indicators 1245 [ 1246 { "instancePath": "", 1247 "schemaPath": "/properties/a" }, 1248 { "instancePath": "/b", 1249 "schemaPath": "/properties/b/type" }, 1250 { "instancePath": "/c", 1251 "schemaPath": "/optionalProperties/c/type" }, 1252 { "instancePath": "/e", 1253 "schemaPath": "" } 1254 ] 1256 If instead the schema had "additionalProperties: true", but was 1257 otherwise the same: 1259 { 1260 "properties": { 1261 "a": { "type": "string" }, 1262 "b": { "type": "string" } 1263 }, 1264 "optionalProperties": { 1265 "c": { "type": "string" }, 1266 "d": { "type": "string" } 1267 }, 1268 "additionalProperties": true 1269 } 1271 And the instance remained the same: 1273 { "b": 3, "c": 3, "e": 3 } 1275 Then the error indicators from evaluating the instance the schema 1276 would be 1278 [ 1279 { "instancePath": "", 1280 "schemaPath": "/properties/a" }, 1281 { "instancePath": "/b", 1282 "schemaPath": "/properties/b/type" }, 1283 { "instancePath": "/c", 1284 "schemaPath": "/optionalProperties/c/type" }, 1285 ] 1287 These are the same errors as before, except the final error 1288 (associated with the additional member named "e" in the instance) is 1289 no longer present. This is because "additionalProperties: true" 1290 enables "allow additional properties" mode on the schema. 1292 3.3.7. Values 1294 The elements form is meant to describe instances that are JSON 1295 objects being used as an associative array. 1297 If a schema is of the values form, then let _S_ be the value of the 1298 schema member with the name "values". The instance is accepted if 1299 and only if all of the following are true: 1301 o The instance is an object. Otherwise, the error indicator for 1302 this case shall have an "instancePath" pointing to the instance, 1303 and a "schemaPath" pointing to the schema member with the name 1304 "values". 1306 o If the instance is an object, then every member value of the 1307 instance must be accepted by _S_. Otherwise, the error indicators 1308 for this case are the union of all the error indicators arising 1309 from evaluating _S_ against member values of the instance. 1311 For example, the schema: 1313 { 1314 "values": { 1315 "type": "float32" 1316 } 1317 } 1319 accepts 1321 {} 1323 and 1325 {"a": 1, "b": 2} 1327 but rejects 1329 false 1331 with the error indicator 1333 [{ "instancePath": "", "schemaPath": "/values" }] 1335 and rejects 1337 { "a": 1, "b": 2, "c": "foo", "d": 3, "e": "bar" } 1339 with the error indicators 1341 [ 1342 { "instancePath": "/c", "schemaPath": "/values/type" }, 1343 { "instancePath": "/e", "schemaPath": "/values/type" } 1344 ] 1346 3.3.8. Discriminator 1348 The discriminator form is meant to describe JSON objects being used 1349 in a fashion similar to a discriminated union construct in C-like 1350 languages. When a schema is of the "discriminator" form, it 1351 validates: 1353 o That the instance is an object, 1355 o That the instance has a particular "tag" property, 1357 o That this "tag" property's value is a string within a set of valid 1358 values, and 1360 o That the instance satisfies another schema, where this other 1361 schema is chosen based on the value of the "tag" property. 1363 The behavior of the discriminator form is more complex than the other 1364 keywords. Readers familiar with CDDL may find the final example in 1365 Appendix B helpful in understanding its behavior. What follows in 1366 this section is a description of the discriminator form's behavior, 1367 as well as some examples. 1369 If a schema is of the "discriminator" form, then: 1371 o Let _D_ be the schema member with the name "discriminator". 1373 o Let _T_ be the member of _D_ with the name "tag". 1375 o Let _M_ be the member of _D_ with the name "mapping". 1377 o Let _I_ be the instance member whose name equals _T_'s value. _I_ 1378 may, for some rejected instances, not exist. 1380 o Let _S_ be the member of _M_ whose name equals _I_'s value. _S_ 1381 may, for some rejected instances, not exist. 1383 The instance is accepted if and only if: 1385 o The instance is an object. 1387 Otherwise, the error indicator for this case shall have an 1388 "instancePath" pointing to the instance, and a "schemaPath" 1389 pointing to _D_. 1391 o If the instance is a JSON object, then _I_ must exist. 1393 Otherwise, the error indicator for this case shall have an 1394 "instancePath" pointing to the instance, and a "schemaPath" 1395 pointing to _T_. 1397 o If the instance is a JSON object and _I_ exists, _I_'s value must 1398 be a string. 1400 Otherwise, the error indicator for this case shall have an 1401 "instancePath" pointing to _I_, and a "schemaPath" pointing to 1402 _T_. 1404 o If the instance is a JSON object and _I_ exists and has a string 1405 value, then _S_ must exist. 1407 Otherwise, the error indicator for this case shall have an 1408 "instancePath" pointing to _I_, and a "schemaPath" pointing to 1409 _M_. 1411 o If the instance is a JSON object, _I_ exists, and _S_ exists, then 1412 the instance must satisfy _S_'s value. By Section 2, _S_'s value 1413 must have the properties form. Apply the "discriminator tag 1414 exemption" afforded in Section 3.3.6 to _I_ when evaluating 1415 whether the instance satisfies _S_'s value. 1417 Otherwise, the error indicators for this case shall be error 1418 indicators from evaluating _S_'s value against the instance, with 1419 the "discriminator tag exemption" applied to _I_. 1421 Each of the list items above are defined to be mutually exclusive. 1422 For the same instance and schema, only one of the list items above 1423 will apply. 1425 For example, the schema: 1427 { 1428 "discriminator": { 1429 "tag": "version", 1430 "mapping": { 1431 "v1": { 1432 "properties": { 1433 "a": { "type": "float32" } 1434 } 1435 }, 1436 "v2": { 1437 "properties": { 1438 "a": { "type": "string" } 1439 } 1440 } 1441 } 1442 } 1443 } 1445 rejects 1447 "example" 1449 with the error indicator 1451 [{ "instancePath": "", "schemaPath": "/discriminator" }] 1453 (This is the case of the instance not being an object.) 1455 Also rejected is 1457 {} 1459 with the error indicator 1461 [{ "instancePath": "", "schemaPath": "/discriminator/tag" }] 1463 (This is the case of _I_ not existing.) 1465 Also rejected is 1467 { "version": 1 } 1469 with the error indicator 1471 [ 1472 { 1473 "instancePath": "/version", 1474 "schemaPath": "/discriminator/tag" 1475 } 1476 ] 1478 (This is the case of _I_ existing, but not having a string value.) 1480 Also rejected is 1482 { "version": "v3" } 1484 with the error indicator 1486 [ 1487 { 1488 "instancePath": "/version", 1489 "schemaPath": "/discriminator/mapping" 1490 } 1491 ] 1493 (This is the case of _I_ existing and having a string value, but _S_ 1494 not existing.) 1496 Also rejected is 1498 { "version": "v2", "a": 3 } 1500 with the error indicator 1502 [ 1503 { 1504 "instancePath": "/a", 1505 "schemaPath": "/discriminator/mapping/v2/properties/a/type" 1506 } 1507 ] 1509 (This is the case of _I_ and _S_ existing, but the instance not 1510 satisfying _S_'s value.) 1512 Finally, the schema accepts 1514 { "version": "v2", "a": "foo" } 1516 This instance is accepted despite the fact that "version" is not 1517 mentioned by "/discriminator/mapping/v2/properties"; the 1518 "discriminator tag exemption" ensures that "version" is not treated 1519 as an additional property when evaluating the instance against _S_'s 1520 value. 1522 To further illustrate the discriminator form with examples, recall 1523 the JDDF schema in Figure 25, reproduced here: 1525 { 1526 "tag": "event_type", 1527 "mapping": { 1528 "account_deleted": { 1529 "properties": { 1530 "account_id": { "type": "string" } 1531 } 1532 }, 1533 "account_payment_plan_changed": { 1534 "properties": { 1535 "account_id": { "type": "string" }, 1536 "payment_plan": { "enum": ["FREE", "PAID"] } 1537 }, 1538 "optionalProperties": { 1539 "upgraded_by": { "type": "string" } 1540 } 1541 } 1542 } 1543 } 1545 This schema accepts 1547 { "event_type": "account_deleted", "account_id": "abc-123" } 1549 and 1551 { 1552 "event_type": "account_payment_plan_changed", 1553 "account_id": "abc-123", 1554 "payment_plan": "PAID" 1555 } 1557 and 1558 { 1559 "event_type": "account_payment_plan_changed", 1560 "account_id": "abc-123", 1561 "payment_plan": "PAID", 1562 "upgraded_by": "users/mkhwarizmi" 1563 } 1565 but rejects 1567 {} 1569 with the error indicator 1571 [{ "instancePath": "", "schemaPath": "/discriminator/tag" }] 1573 and rejects 1575 { "event_type": "some_other_event_type" } 1577 with the error indicator 1579 [ 1580 { 1581 "instancePath": "/event_type", 1582 "schemaPath": "/discriminator/mapping" 1583 } 1584 ] 1586 and rejects 1588 { "event_type": "account_deleted" } 1590 with the error indicator 1592 [{ 1593 "instancePath": "", 1594 "schemaPath": 1595 "/discriminator/mapping/account_deleted/properties/account_id" 1596 }] 1598 and rejects 1600 { 1601 "event_type": "account_payment_plan_changed", 1602 "account_id": "abc-123", 1603 "payment_plan": "PAID", 1604 "xxx": "asdf" 1605 } 1607 with the error indicator 1609 [{ 1610 "instancePath": "/xxx", 1611 "schemaPath": 1612 "/discriminator/mapping/account_payment_plan_changed" 1613 }] 1615 4. IANA Considerations 1617 No IANA considerations. 1619 5. Security Considerations 1621 Implementations of JDDF will necessarily be manipulating JSON data. 1622 Therefore, the security considerations of [RFC8259] are all relevant 1623 here. 1625 Implementations which evaluate user-inputted schemas SHOULD implement 1626 mechanisms to detect, and abort, circular references which might 1627 cause a naive implementation to go into an infinite loop. Without 1628 such mechanisms, implementations may be vulnerable to denial-of- 1629 service attacks. 1631 6. References 1633 6.1. Normative References 1635 [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate 1636 Requirement Levels", BCP 14, RFC 2119, 1637 DOI 10.17487/RFC2119, March 1997, 1638 . 1640 [RFC3339] Klyne, G. and C. Newman, "Date and Time on the Internet: 1641 Timestamps", RFC 3339, DOI 10.17487/RFC3339, July 2002, 1642 . 1644 [RFC6901] Bryan, P., Ed., Zyp, K., and M. Nottingham, Ed., 1645 "JavaScript Object Notation (JSON) Pointer", RFC 6901, 1646 DOI 10.17487/RFC6901, April 2013, 1647 . 1649 [RFC8174] Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC 1650 2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174, 1651 May 2017, . 1653 [RFC8259] Bray, T., Ed., "The JavaScript Object Notation (JSON) Data 1654 Interchange Format", STD 90, RFC 8259, 1655 DOI 10.17487/RFC8259, December 2017, 1656 . 1658 [RFC8610] Birkholz, H., Vigano, C., and C. Bormann, "Concise Data 1659 Definition Language (CDDL): A Notational Convention to 1660 Express Concise Binary Object Representation (CBOR) and 1661 JSON Data Structures", RFC 8610, DOI 10.17487/RFC8610, 1662 June 2019, . 1664 6.2. Informative References 1666 [I-D.handrews-json-schema] 1667 Wright, A., Andrews, H., Hutton, B., and G. Dennis, "JSON 1668 Schema: A Media Type for Describing JSON Documents", 1669 draft-handrews-json-schema-02 (work in progress), 1670 September 2019. 1672 [OPENAPI] OpenAPI Initiative, "OpenAPI Specification", October 2019, 1673 . 1675 [RFC7071] Borenstein, N. and M. Kucherawy, "A Media Type for 1676 Reputation Interchange", RFC 7071, DOI 10.17487/RFC7071, 1677 November 2013, . 1679 [RFC7493] Bray, T., Ed., "The I-JSON Message Format", RFC 7493, 1680 DOI 10.17487/RFC7493, March 2015, 1681 . 1683 Appendix A. Other Considerations 1685 This appendix is not normative. 1687 This section describes possible features which are intentionally left 1688 out of JSON Data Definition Format, and justifies why these features 1689 are omitted. 1691 A.1. Support for 64-bit Numbers 1693 This document does not allow "int64" or "uint64" as values for the 1694 JDDF "type" keyword (see Figure 11 and Section 3.3.3). Such 1695 hypothetical "int64" or "uint64" types would behave like "int32" or 1696 "uint32" (respectively), but with the range of values associated with 1697 64-bit instead of 32-bit integers, that is: 1699 o "int64" would accept numbers between -(2**63) and (2**63)-1 1700 o "uint64" would accept numbers between 0 and (2**64)-1 1702 Users of "int64" and "uint64" would likely expect that the full range 1703 of signed or unsigned 64-bit integers could interoperably be 1704 transmitted as JSON without loss of precision. But this assumption 1705 is likely to be incorrect, for the reasons given in Section 2.2 of 1706 [RFC7493]. 1708 "int64" and "uint64" likely would have led users to falsely assume 1709 that the full range of 64-bit integers can be interoperably procesed 1710 as JSON without loss of precision. To avoid leading users astray, 1711 JDDF omits "int64" and "uint64". 1713 A.2. Support for Non-Root Schemas 1715 This document disallows the "definitions" keyword from appearing 1716 outside of root schemas (see Figure 1). Conceivably, this document 1717 could have instead allowed "definitions" to appear on any schema, 1718 even non-root ones. Under this alternative design, "ref"s would 1719 resolve to a definition in the "nearest" (i.e., most nested) schema 1720 which both contained the "ref" and which had a suitably-named 1721 "definitions" member. 1723 For instance, under this alternative approach, one could define 1724 schemas like the one in Figure 30: 1726 { 1727 "properties": { 1728 "foo": { 1729 "definitions": { 1730 "user": { "properties": { "user_id": {"type": "string" }}} 1731 }, 1732 "ref": "user" 1733 }, 1734 "bar": { 1735 "definitions": { 1736 "user": { "properties": { "user_id": {"type": "string" }}} 1737 }, 1738 "ref": "user" 1739 }, 1740 "baz": { 1741 "definitions": { 1742 "user": { "properties": { "userId": {"type": "string" }}} 1743 }, 1744 "ref": "user" 1745 } 1746 } 1747 } 1749 Figure 30: A hypothetical schema had this document permitted non-root 1750 definitions. This is not a correct JDDF schema. 1752 If schemas like that in Figure 30 were permitted, code generation 1753 from JDDF schemas would be more difficult, and the generated code 1754 would be less useful. 1756 Code generation would be more difficult because it would force code 1757 generators to implement a name mangling scheme for types generated 1758 from definitions. This additional difficulty is not immense, but 1759 adds complexity to an otherwise relatively trivial task. 1761 Generated code would be less useful because generated, mangled struct 1762 names are less pithy than human-defined struct names. For instance, 1763 the "user" definitions in Figure 30 might have been generated into 1764 types named "PropertiesFooUser", "PropertiesBarUser", and 1765 "PropertiesBazUser"; obtuse names like these are less useful to 1766 human-written code than names like "User". 1768 Furthermore, even though "PropertiesFooUser" and "PropertiesBarUser" 1769 would be essentially identical, they would not be interchangeable in 1770 many statically-typed programming languages. A code generator could 1771 attempt to circumvent this by deduplicating identical definitions, 1772 but then the user might be confused as to why the subtly distinct 1773 "PropertiesBazUser", defined from a schema allowing a property named 1774 "userId" (not "user_id"), was not deduplicated. 1776 Because there seem to be implementation and usability challenges 1777 associated with non-root definitions, and because it would be easier 1778 to later amend JDDF to permit for non-root definitions than to later 1779 amend JDDF to prohibit them, this document does not permit non-root 1780 definitions in JDDF schemas. 1782 Appendix B. Comparison with CDDL 1784 This appendix is not normative. 1786 To aid the reader familiar with CDDL, this section illustrates how 1787 JDDF works by presenting JDDF schemas and CDDL schemas which accept 1788 and reject the same instances. 1790 The JDDF schema: 1792 {} 1794 accepts the same instances as the CDDL rule: 1796 root = any 1798 The JDDF schema: 1800 { 1801 "definitions": { 1802 "a": { "elements": { "ref": "b" }}, 1803 "b": { "type": "float32" } 1804 }, 1805 "elements": { 1806 "ref": "a" 1807 } 1808 } 1810 accepts the same instances as the CDDL rule: 1812 root = [* a] 1814 a = [* b] 1815 b = number 1817 The JDDF schema: 1819 { "enum": ["PENDING", "DONE", "CANCELED"]} 1821 accepts the same instances as the CDDL rule: 1823 root = "PENDING" / "DONE" / "CANCELED" 1825 The JDDF schema: 1827 {"type": "boolean"} 1829 accepts the same instances as the CDDL rule: 1831 root = bool 1833 The JDDF schemas: 1835 {"type": "float32"} 1837 and 1839 {"type": "float64"} 1841 both accept the same instances as the CDDL rule: 1843 root = number 1845 The JDDF schema: 1847 {"type": "string"} 1849 accepts the same instances as the CDDL rule: 1851 root = tstr 1853 The JDDF schema: 1855 {"type": "timestamp"} 1857 accepts the same instances as the CDDL rule: 1859 root = tdate 1861 The JDDF schema: 1863 { "elements": { "type": "float32" }} 1865 accepts the same instances as the CDDL rule: 1867 root = [* number] 1869 The JDDF schema: 1871 { 1872 "properties": { 1873 "a": { "type": "boolean" }, 1874 "b": { "type": "float32" } 1875 }, 1876 "optionalProperties": { 1877 "c": { "type": "string" }, 1878 "d": { "type": "timestamp" } 1879 } 1880 } 1882 accepts the same instances as the CDDL rule: 1884 root = { a: bool, b: number, ? c: tstr, ? d: tdate } 1886 The JDDF schema: 1888 { "values": { "type": "float32" }} 1890 accepts the same instances as the CDDL rule: 1892 root = { * tstr => number } 1894 Finally, the JDDF schema: 1896 { 1897 "discriminator": { 1898 "tag": "a", 1899 "mapping": { 1900 "foo": { 1901 "properties": { 1902 "b": { "type": "float32" } 1903 } 1904 }, 1905 "bar": { 1906 "properties": { 1907 "b": { "type": "string" } 1908 } 1909 } 1910 } 1911 } 1912 } 1914 accepts the same instances as the CDDL rule: 1916 root = { a: "foo", b: number } / { a: "bar", b: tstr } 1918 Appendix C. Examples 1920 This appendix is not normative. 1922 As a demonstration of JDDF, in Figure 31 is a JDDF schema closely 1923 equivalent to the plain-English definition "reputation-object" 1924 described in Section 6.2.2 of [RFC7071]: 1926 { 1927 "properties": { 1928 "application": { "type": "string" }, 1929 "reputons": { 1930 "elements": { 1931 "additionalProperties": true, 1932 "properties": { 1933 "rater": { "type": "string" }, 1934 "assertion": { "type": "string" }, 1935 "rated": { "type": "string" }, 1936 "rating": { "type": "float32" }, 1937 }, 1938 "optionalProperties": { 1939 "confidence": { "type": "float32" }, 1940 "normal-rating": { "type": "float32" }, 1941 "sample-size": { "type": "float64" }, 1942 "generated": { "type": "float64" }, 1943 "expires": { "type": "float64" } 1944 } 1945 } 1946 } 1947 } 1948 } 1950 Figure 31: A JDDF schema describing "reputation-object" from 1951 Section 6.6.2 of [RFC7071] 1953 This schema does not enforce the requirement that "sample-size", 1954 "generated", and "expires" be unbounded positive integers. It does 1955 not express the limitation that "rating", "confidence", and "normal- 1956 rating" should not have more than three decimal places of precision. 1958 The example in Figure 31 can be compared against the equivalent 1959 example in Appendix H of [RFC8610]. 1961 Acknowledgments 1963 Carsten Bormann provided lots of useful guidance and feedback on 1964 JDDF's design and the structure of this document. 1966 Tim Bray suggested the current "ref" model, and the addition of 1967 "enum". Anders Rundgren suggested extending "type" to have more 1968 support for numerical types. James Manger suggested additional 1969 clarifying examples of how integer types work. Members of the IETF 1970 JSON mailing list - in particular, Pete Cordell, Phillip Hallam- 1971 Baker, Nico Williams, John Cowan, Rob Sayre, and Erik Wilde - 1972 provided lots of useful feedback. 1974 OpenAPI's "discriminator" object [OPENAPI] inspired the 1975 "discriminator" form. [I-D.handrews-json-schema] influenced various 1976 parts of JDDF's early design. 1978 Author's Address 1980 Ulysse Carion 1981 Segment.io, Inc 1982 100 California Street 1983 San Francisco 94111 1984 United States of America 1986 Email: ulysse@segment.com