<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE rfc SYSTEM "rfc2629-xhtml.ent">

<rfc number="8927" xmlns:xi="http://www.w3.org/2001/XInclude"
     ipr="trust200902" docName="draft-ucarion-json-type-definition-04"
     category="exp" obsoletes="" updates="" submissionType="independent"
     xml:lang="en" tocInclude="true" sortRefs="true" symRefs="true"
     version="3" > 


  <front>
    <title>JSON Type Definition</title>
    <seriesInfo name="RFC" value="8927"/>
    <author initials="U." surname="Carion" fullname="Ulysse Carion">
      <organization abbrev="Segment">Segment.io, Inc</organization>
      <address>
        <postal>
          <street>100 California Street</street>
          <city>San Francisco</city>
	  <region>CA</region>
          <code>94111</code>
          <country>United States of America</country>
        </postal>
        <email>ulysse@segment.com</email>
      </address>
    </author>
    <date year="2020" month="November"/>
    <area>Applications</area>
    <workgroup>Independent Submission</workgroup>

<keyword>data interchange format</keyword>

<keyword>description language
</keyword>

<keyword>schema language
</keyword>

<keyword>tree grammar
</keyword>




    <abstract>
      <t>This document proposes a format, called JSON Type Definition (JTD),
      for describing the shape of JavaScript Object Notation (JSON)
      messages. Its main goals are to enable code generation from schemas as
      well as portable validation with standardized error indicators. To this
      end, JTD is intentionally limited to be no more expressive than the type
      systems of mainstream programming languages. This intentional
      limitation, as well as the decision to make JTD schemas be JSON
      documents, makes tooling atop of JTD easier to build.</t>
      <t>This document does not have IETF consensus and is presented here to
      facilitate experimentation with the concept of JTD.</t>
    </abstract>
  </front>
  <middle>
    <section anchor="introduction" numbered="true" toc="default">
      <name>Introduction</name>
      <t>This document describes a schema language for JSON <xref
      target="RFC8259" format="default"/> called JSON Type Definition
      (JTD).</t> 
      <t>There exist many options for describing JSON data. JTD's niche is to
      focus on enabling code generation from schemas; to this end, JTD's
      expressiveness is intentionally limited to be no more powerful than what
      can be expressed in the type systems of mainstream programming
      languages.</t> 
      <t>The goals of JTD are to:</t>
      <ul spacing="normal">
        <li>Provide an unambiguous description of the overall structure of a
	JSON document.</li>
        <li>Be able to describe common JSON data types and structures (that
        is, the data types and structures necessary to support most JSON
        documents and that are widely understood in an interoperable way by
        JSON implementations).</li>
        <li>Provide a single format that is readable and editable by both
	humans and machines and that can be embedded within other JSON
	documents. This makes JTD a convenient format for tooling to accept as
	input or produce as output.</li>
        <li>Enable code generation from JTD schemas. JTD schemas are meant to
	be easy to convert into data structures idiomatic to mainstream
	programming languages.</li> 
        <li>Provide a standardized format for error indicators when data does
	not conform with a schema.</li>
      </ul>
      <t>JTD is intentionally designed as a rather minimal schema
      language. Thus, although JTD can describe some categories of JSON, it is
      not able to describe its own structure; this document uses Concise Data
      Definition Language (CDDL) <xref target="RFC8610" format="default"/> to
      describe JTD's syntax. By keeping the expressiveness of the schema
      language minimal, JTD makes code generation and standardized error
      indicators easier to implement.</t> 
      <t>Examples in this document use constructs from the C++ programming
      language. These examples are provided to aid the reader in understanding
      the principles of JTD but are not limiting in any way.</t>
      <t>JTD's feature set is designed to represent common patterns in
      JSON-using applications, while still having a clear correspondence to
      programming languages in widespread use. Thus, JTD supports:</t>
      <ul spacing="normal">
        <li>Signed and unsigned 8-, 16-, and 32-bit integers. A tool that
	converts JTD schemas into code can use "int8_t",
	"uint8_t", "int16_t", etc., or their equivalents in the
	target language, to represent these JTD types.</li> 
        <li>A distinction between "float32" and "float64". Code
	generators can use "float" and "double", or their
	equivalents, for these JTD types.</li> 
        <li>A "properties" form of JSON objects, corresponding to some sort of
	struct or record. The "properties" form of JSON objects is akin to a
	C++ "struct".</li> 
        <li>A "values" form of JSON objects, corresponding to some sort of
	dictionary or associative array. The "values" form of JSON objects is
	akin to a C++ "std::map".</li> 
        <li>A "discriminator" form of JSON objects, corresponding to a
	discriminated (or "tagged") union. The "discriminator" form of JSON
	objects is akin to a C++ "std::variant".</li>
      </ul>
      <t>The principle of common patterns in JSON is why JTD does not support
      64-bit integers, as these are usually transmitted over JSON in
      non-interoperable (i.e., ignoring the recommendations in <xref
      target="RFC7493" sectionFormat="of" section="2.2"/>) or mutually
      inconsistent ways. <xref target="other-considerations-int64"
      format="default"/> further elaborates on why JTD does not support 64-bit
      integers.</t>
      <t>The principle of clear correspondence to common programming languages
      is why JTD does not support, for example, a data type for integers up to
      2**53-1.</t> 
      <t>It is expected that for many use cases, a schema language of JTD's
      expressiveness is sufficient. Where a more expressive language is
      required, alternatives exist in CDDL and others.</t>
      <t>This document does not have IETF consensus and is presented here to
      facilitate experimentation with the concept of JTD. The purpose of the
      experiment is to gain experience with JTD and to possibly revise this
      work accordingly.  If JTD is determined to be a valuable and popular
      approach, it may be taken to the IETF for further discussion and
      revision.</t> 
      <t>This document has the following structure.
      <xref target="syntax" format="default"/> defines the syntax of
      JTD. <xref target="semantics" format="default"/> describes the semantics
      of JTD; this includes determining whether some data satisfies a schema
      and what error indicators should be produced when the data is
      unsatisfactory. <xref target="other-considerations" format="default"/>
      discusses why certain features are omitted from JTD. <xref
      target="comparison-with-cddl" format="default"/> presents various JTD
      schemas and their CDDL equivalents.</t>
      <section anchor="terminology" numbered="true" toc="default">
        <name>Terminology</name>
        <t>
    The key words "<bcp14>MUST</bcp14>", "<bcp14>MUST NOT</bcp14>",
    "<bcp14>REQUIRED</bcp14>", "<bcp14>SHALL</bcp14>", "<bcp14>SHALL
    NOT</bcp14>", "<bcp14>SHOULD</bcp14>", "<bcp14>SHOULD NOT</bcp14>",
    "<bcp14>RECOMMENDED</bcp14>", "<bcp14>NOT RECOMMENDED</bcp14>",
    "<bcp14>MAY</bcp14>", and "<bcp14>OPTIONAL</bcp14>" in this document are
    to be interpreted as
    described in BCP&nbsp;14 <xref target="RFC2119"/> <xref target="RFC8174"/> 
    when, and only when, they appear in all capitals, as shown here.
        </t>

        <t>The term "JSON Pointer", when it appears in this document, is to be
	understood as it is defined in <xref target="RFC6901"
	format="default"/>.</t> 
        <t>The terms "object", "member", "array", "number", "name", and
	"string" in this document are to be interpreted as described in <xref
	target="RFC8259" format="default"/>.</t> 
        <t>The term "instance", when it appears in this document, refers to a
	JSON value being validated against a JTD schema. This value can be an
	entire JSON document, or it can be a value embedded within a JSON
	document.</t> 
      </section>
      <section anchor="scope-of-experiment" numbered="true" toc="default">
        <name>Scope of Experiment</name>
        <t>JTD is an experiment. Participation in this experiment consists of
	using JTD to validate or document interchanged JSON messages or
	building tooling atop of JTD. Feedback on the results of this
	experiment may be emailed to the author. Participants in this
	experiment are anticipated to mostly be nodes that provide or consume
	JSON-based APIs.</t> 
        <t>Nodes know if they are participating in the experiment if they are
	validating JSON messages against a JTD schema or if they are relying
	on another node to do so. Nodes are also participating in the
	experiment if they are running code generated from a JTD schema.</t> 
        <t>The risk of this experiment "escaping" takes the form of a
	JTD-supporting node expecting another node, which lacks such support,
	to validate messages against some JTD schema. In such a case, the
	outcome will likely be that the nodes fail to interchange information correctly.</t>
        <t>This experiment will be deemed successful when JTD has been
	implemented by multiple independent parties and these parties
	successfully use JTD to facilitate information interchange within
	their internal systems or between systems operated by independent parties.</t>
        <t>If this experiment is deemed successful, and JTD is determined to
	be a valuable and popular approach, it may be taken to the IETF for
	further discussion and revision. One possible outcome of this
	discussion and revision could be that a working group produces a
	Standards Track specification of JTD.</t> 
        <t>Some implementations of JTD, as well as code generators and other
	tooling related to JTD, are available at
	<eref target="https://github.com/jsontypedef" brackets="angle"/>.</t> 
      </section>
    </section>
    <section anchor="syntax" numbered="true" toc="default">
      <name>Syntax</name>
      <t>This section describes when a JSON document is a correct JTD
      schema. Because Concise Data Definition Language (CDDL) is well suited
      to the task of defining complex JSON formats, such as JTD schemas, this
      section uses CDDL to describe the format of JTD schemas.</t>
      <t>JTD schemas may recursively contain other schemas. In this document,
      a "root schema" is one that is not contained within another schema,
      i.e., it is "top level".</t>
      <t>A JTD schema is a JSON object taking on an appropriate form. JTD
      schemas may contain "additional data", discussed in <xref
      target="extending-JTD-syntax" format="default"/>. Root JTD schemas may
      optionally contain definitions (a mapping from names to schemas).</t>
      <t>A correct root JTD schema <bcp14>MUST</bcp14> match the "root-schema" CDDL
      rule described in this section. A correct non-root JTD schema <bcp14>MUST</bcp14> match
      the "schema" CDDL rule described in this section.</t>
      <figure anchor="cddl-schema">
        <name>CDDL Definition of a Schema</name>
<sourcecode type="cddl">
; root-schema is identical to schema, but additionally allows for
; definitions.
;
; definitions are prohibited from appearing on non-root schemas.
root-schema = {
  ? definitions: { * tstr => { schema}},
  schema,
}
; schema is the main CDDL rule defining a JTD schema.
;
; All JTD schemas are JSON objects taking on one of eight forms
; listed here.
schema = (
  ref //
  type //
  enum //
  elements //
  properties //
  values //
  discriminator //
  empty //
)
; shared is a CDDL rule containing properties that all eight schema
; forms share.
shared = (
  ? metadata: { * tstr => any },
  ? nullable: bool,
)
; empty describes the "empty" schema form.
empty = shared
; ref describes the "ref" schema form.
;
; There are additional constraints on this form that cannot be
; expressed in CDDL. Section 2.2.2 describes these additional
; constraints in detail.
ref = ( ref: tstr, shared )
; type describes the "type" schema form.
type = (
  type: "boolean"
    / "float32"
    / "float64"
    / "int8"
    / "uint8"
    / "int16"
    / "uint16"
    / "int32"
    / "uint32"
    / "string"
    / "timestamp",
  shared,
)
; enum describes the "enum" schema form.
;
; There are additional constraints on this form that cannot be
; expressed in CDDL. Section 2.2.4 describes these additional
; constraints in detail.
enum = ( enum: [+ tstr], shared )
; elements describes the "elements" schema form.
elements = ( elements: { schema }, shared )
; properties describes the "properties" schema form.
;
; This CDDL rule is defined so that a schema of the "properties" form
; may omit a member named "properties" or a member named
; "optionalProperties", but not both.
;
; There are additional constraints on this form that cannot be
; expressed in CDDL. Section 2.2.6 describes these additional
; constraints in detail.
properties = (with-properties // with-optional-properties)
with-properties = (
  properties: { * tstr => { schema }},
  ? optionalProperties: { * tstr => { schema }},
  ? additionalProperties: bool,
  shared,
)
with-optional-properties = (
  ? properties: { * tstr => { schema }},
  optionalProperties: { * tstr => { schema }},
  ? additionalProperties: bool,
  shared,
)
; values describes the "values" schema form.
values = ( values: { schema }, shared )
; discriminator describes the "discriminator" schema form.
;
; There are additional constraints on this form that cannot be
; expressed in CDDL. Section 2.2.8 describes these additional
; constraints in detail.
discriminator = (
  discriminator: tstr,
  ; Note well: this rule is defined in terms of the "properties"
  ; CDDL rule, not the "schema" CDDL rule.
  mapping: { * tstr => { properties } }
  shared,
)
</sourcecode>

      </figure>
      <t>The remainder of this section will describe constraints on JTD
      schemas that cannot be expressed in CDDL. It will also provide examples of
      valid and invalid JTD schemas.</t>
      <section anchor="root-vs-non-root-schemas" numbered="true" toc="default">
        <name>Root vs. Non-root Schemas</name>
        <t>The "root-schema" rule in <xref target="cddl-schema"
	format="default"/> permits a member named "definitions",
	but the "schema" rule does not permit for such a member. This
	means that only root (i.e., "top-level") JTD schemas can have a
	"definitions" object, and subschemas may not.</t>
        <t>Thus,</t>
<sourcecode type="json">
   { "definitions": {} }
</sourcecode> 
        <t>is a correct JTD schema, but</t>
<sourcecode type="json"> 
   {
     "definitions": {
       "foo": {
         "definitions": {}
       }
     }
   }
</sourcecode> 
        <t>is not, because subschemas (such as the object at
	"/definitions/foo") must not have a member named
	"definitions".</t> 
      </section>
      <section anchor="forms" numbered="true" toc="default">
        <name>Forms</name>
        <t>JTD schemas (i.e., JSON objects satisfying the "schema" CDDL
	rule in <xref target="cddl-schema" format="default"/>) must take on
	one of eight forms. These forms are defined so as to be mutually
	exclusive; a schema cannot satisfy multiple forms at once.</t> 
        <section anchor="syntax-form-empty" numbered="true" toc="default">
          <name>Empty</name>
          <t>The "empty" form is defined by the "empty" CDDL
	  rule in <xref target="cddl-schema" format="default"/>. The semantics
	  of the "empty" form are described in <xref
	  target="semantics-form-empty" format="default"/>.</t> 
          <t>Despite the name "empty", schemas of the "empty" form are
	  not necessarily empty JSON objects. Like schemas of any of the eight
	  forms, schemas of the "empty" form may contain members named
	  "nullable" (whose value must be "true" or
	  "false") or "metadata" (whose value must be an object)
	  or both.</t> 
          <t>Thus,</t>

<sourcecode type="json"> 
   {}
</sourcecode> 
          <t>and</t>
<sourcecode type="json"> 
   { "nullable": true }
</sourcecode> 
          <t>and</t>
<sourcecode type="json"> 
   { "nullable": true, "metadata": { "foo": "bar" }}
</sourcecode> 
          <t>are correct JTD schemas of the "empty" form, but</t>
<sourcecode type="json">  
   { "nullable": "foo" }
</sourcecode> 
          <t>is not, because the value of the member named "nullable"
	  must be "true" or "false".</t>
        </section>
        <section anchor="syntax-form-ref" numbered="true" toc="default">
          <name>Ref</name>
          <t>The "ref" form is defined by the "ref" CDDL rule in
	  <xref target="cddl-schema" format="default"/>. The semantics of the
	  "ref" form are described in <xref target="semantics-form-ref"
	  format="default"/>.</t> 
          <t>For a schema of the "ref" form to be correct, the value of
	  the member named "ref" must refer to one of the definitions
	  found at the root level of the schema it appears in. More formally,
	  for a schema <em>S</em> of the "ref" form:</t>
          <ul spacing="normal">
            <li>Let <em>B</em> be the root schema containing the schema or
	    the schema itself if it is a root schema.</li>
            <li>Let <em>R</em> be the value of the member of <em>S</em> with
	    the name "ref".</li> 
          </ul>
          <t>If the schema is correct, then <em>B</em> <bcp14>MUST</bcp14> have a member
	  <em>D</em> with the name "definitions", and <em>D</em> <bcp14>MUST</bcp14>
	  contain a member whose name equals <em>R</em>.</t> 
          <t>Thus,</t>
<sourcecode type="json"> 
   {
     "definitions": {
       "coordinates": {
         "properties": {
           "lat": { "type": "float32" },
           "lng": { "type": "float32" }
         }
       }
     },
     "properties": {
       "user_location": { "ref": "coordinates" },
       "server_location": { "ref": "coordinates" }
     }
   }
</sourcecode> 
          <t>is a correct JTD schema and demonstrates the point of the
	  "ref" form: to avoid redefining the same thing
	  twice. However,</t> 
<sourcecode type="json"> 
   { "ref": "foo" }
</sourcecode> 
          <t>is not a correct JTD schema, as there are no top-level
	  "definitions", and so the "ref" form cannot be
	  correct. Similarly,</t> 
<sourcecode type="json"> 
   { "definitions": { "foo": {}}, "ref": "bar" }
</sourcecode> 
          <t>is not a correct JTD schema, as there is no member named
	  "bar" in the top-level "definitions".</t>
        </section>
        <section anchor="syntax-form-type" numbered="true" toc="default">
          <name>Type</name>
          <t>The "type" form is defined by the "type" CDDL rule
	  in <xref target="cddl-schema" format="default"/>. The semantics of
	  the "type" form are described in <xref
	  target="semantics-form-type" format="default"/>.</t> 
          <t>As an example of a correct JTD schema of the "type"
	  form,</t> 
<sourcecode type="json"> 
   { "type": "uint8" }
</sourcecode> 
          <t>is a correct JTD schema, whereas</t>
<sourcecode type="json"> 
   { "type": true }
</sourcecode> 
          <t>and</t>
<sourcecode type="json"> 
   { "type": "foo" }
</sourcecode> 
          <t>are not correct schemas, as neither "true" nor the JSON
	  string "foo" are in the list of permitted values of the
	  "type" member described in the "type" CDDL rule in
	  <xref target="cddl-schema" format="default"/>.</t> 
        </section>
        <section anchor="syntax-form-enum" numbered="true" toc="default">
          <name>Enum</name>
          <t>The "enum" form is defined by the "enum" CDDL rule
	  in <xref target="cddl-schema" format="default"/>. The semantics of
	  the "enum" form are described in <xref
	  target="semantics-form-enum" format="default"/>.</t> 
          <t>For a schema of the "enum" form to be correct, the value
	  of the member named "enum" must be a nonempty array of
	  strings, and that array must not contain duplicate values. More
	  formally, for a schema <em>S</em> of the "enum" form:</t>
          <ul spacing="normal">
            <li>Let <em>E</em> be the value of the member of <em>S</em> with
	    name "enum".</li> 
          </ul>
          <t>If the schema is correct, then there <bcp14>MUST NOT</bcp14> exist any pair of
	  elements of <em>E</em> that encode equal string values, where
	  string equality is defined as in <xref
	  target="RFC8259" sectionFormat="of" section="8.3"/>.</t> 
          <t>Thus,</t>
<sourcecode type="json"> 
   { "enum": [] }
</sourcecode> 
          <t>is not a correct JTD schema, as the value of the member named
	  "enum" must be nonempty, and</t>
<sourcecode type="json"> 
   { "enum": ["a\\b", "a\u005Cb"] }
</sourcecode> 
          <t>is not a correct JTD schema, as</t>
<sourcecode type="json"> 
   "a\\b"
</sourcecode> 
          <t>and</t>
<sourcecode type="json"> 
   "a\u005Cb"
</sourcecode> 
          <t>encode strings that are equal by the definition of string
	  equality given in <xref target="RFC8259"
	  sectionFormat="of" section="8.3"/>. By contrast,</t> 
<sourcecode type="json"> 
   { "enum": ["PENDING", "IN_PROGRESS", "DONE" ]}
</sourcecode> 
          <t>is an example of a correct JTD schema of the "enum"
	  form.</t> 
        </section>
        <section anchor="syntax-form-elements" numbered="true" toc="default">
          <name>Elements</name>
          <t>The "elements" form is defined by the "elements"
	  CDDL rule in <xref target="cddl-schema" format="default"/>. The
	  semantics of the "elements" form are described in <xref
	  target="semantics-form-elements" format="default"/>.</t> 
          <t>As an example of a correct JTD schema of the "elements"
	  form,</t> 
<sourcecode type="json"> 
   { "elements": { "type": "uint8" }}
</sourcecode> 
          <t>is a correct JTD schema, whereas</t>
<sourcecode type="json"> 
   { "elements": true }
</sourcecode> 
          <t>and</t>
<sourcecode type="json"> 
   { "elements": { "type": "foo" } }
</sourcecode> 
          <t>are not correct schemas, as neither</t>
<sourcecode type="json"> 
   true
</sourcecode> 
          <t>nor</t>
<sourcecode type="json"> 
   { "type": "foo" }
</sourcecode> 
          <t>are correct JTD schemas, and the value of the member named
	  "elements" must be a correct JTD schema.</t>
        </section>
        <section anchor="syntax-form-properties" numbered="true" toc="default">
          <name>Properties</name>
          <t>The "properties" form is defined by the
	  "properties" CDDL rule in <xref target="cddl-schema"
	  format="default"/>. The semantics of the "properties" form
	  are described in <xref target="semantics-form-props"
	  format="default"/>.</t> 
          <t>For a schema of the "properties" form to be correct,
	  properties must either be required (i.e., in "properties") or
	  optional (i.e., in "optionalProperties"), but not both.</t> 
          <t>More formally, if a schema has both a member named
          "properties" (with value <em>P</em>) and another member named
          "optionalProperties" (with value <em>O</em>), then <em>O</em>
          and <em>P</em> <bcp14>MUST NOT</bcp14> have any member names in
          common; that is, no member of <em>P</em> may have a name equal to
          the name of any member of <em>O</em>, under the definition of string
          equality given in <xref target="RFC8259" sectionFormat="of"
          section="8.3"/>.</t>
          <t>Thus,</t>
<sourcecode type="json"> 
   {
     "properties": { "confusing": {} },
     "optionalProperties": { "confusing": {} }
   }
</sourcecode> 
          <t>is not a correct JTD schema, as "confusing" appears in
	  both "properties" and "optionalProperties". By
	  contrast,</t> 
<sourcecode type="json"> 
   {
     "properties": {
       "users": {
         "elements": {
           "properties": {
             "id": { "type": "string" },
             "name": { "type": "string" },
             "create_time": { "type": "timestamp" }
           },
           "optionalProperties": {
             "delete_time": { "type": "timestamp" }
           }
         }
       },
       "next_page_token": { "type": "string" }
     }
   }
</sourcecode> 
          <t>is a correct JTD schema of the "properties" form,
	  describing a paginated list of users and demonstrating the recursive
	  nature of the syntax of JTD schemas.</t> 
        </section>
        <section anchor="syntax-form-values" numbered="true" toc="default">
          <name>Values</name>
          <t>The "values" form is defined by the "values" CDDL
	  rule in <xref target="cddl-schema" format="default"/>. The semantics
	  of the "values" form are described in <xref
	  target="semantics-form-values" format="default"/>.</t> 
          <t>As an example of a correct JTD schema of the "values"
	  form,</t> 
<sourcecode type="json"> 
   { "values": { "type": "uint8" }}
</sourcecode> 
          <t>is a correct JTD schema, whereas</t>
<sourcecode type="json"> 
   { "values": true }
</sourcecode> 
          <t>and</t>
<sourcecode type="json"> 
   { "values": { "type": "foo" } }
</sourcecode> 
          <t>are not correct schemas, as neither</t>
<sourcecode type="json"> 
   true
</sourcecode> 
          <t>nor</t>
<sourcecode type="json"> 
   { "type": "foo" }
</sourcecode> 
          <t>are correct JTD schemas, and the value of the member named
	  "values" must be a correct JTD schema.</t>
        </section>
        <section anchor="syntax-form-discriminator" numbered="true" toc="default">
          <name>Discriminator</name>
          <t>The "discriminator" form is defined by the
	  "discriminator" CDDL rule in <xref target="cddl-schema"
	  format="default"/>. The semantics of the "discriminator" form
	  are described in <xref target="semantics-form-discriminator"
	  format="default"/>. Understanding the semantics of the
	  "discriminator" form will likely aid the reader in
	  understanding why this section provides constraints on the
	  "discriminator" form beyond those in <xref
	  target="cddl-schema" format="default"/>.</t> 
          <t>To prevent ambiguous or unsatisfiable constraints on the
	  "discriminator" property of a tagged union, an additional
	  constraint on schemas of the "discriminator" form exists. For
	  schemas of the "discriminator" form:</t>
          <ul spacing="normal">
            <li>Let <em>D</em> be the member of the schema with the name
	    "discriminator".</li> 
            <li>Let <em>M</em> be the member of the schema with the name
	    "mapping".</li> 
          </ul>
          <t>If the schema is correct, then all member values <em>S</em> of
	  <em>M</em> will be schemas of the "properties" form. For each
	  <em>S</em>:</t> 
          <ul spacing="normal">
            <li>If <em>S</em> has a member <em>N</em> whose name equals
	    "nullable", <em>N</em>'s value <bcp14>MUST NOT</bcp14> be the JSON
	    primitive value "true".</li> 
            <li>For each member <em>P</em> of <em>S</em> whose name equals
	    "properties" or "optionalProperties", <em>P</em>'s
	    value, which must be an object, <bcp14>MUST NOT</bcp14> contain any members whose
	    name equals <em>D</em>'s value.</li> 
          </ul>
          <t>Thus,</t>
<sourcecode type="json"> 
   {
     "discriminator": "event_type",
     "mapping": {
       "can_the_object_be_null_or_not?": {
         "nullable": true,
         "properties": { "foo": { "type": "string" } }}
       }
     }
   }
</sourcecode> 
          <t>is an incorrect schema, as a member of "mapping" has a
	  member named "nullable" whose value is "true". This
	  would suggest that the instance may be null. Yet, the top-level
	  schema lacks such a "nullable" set to "true", which
	  would suggest that the instance in fact cannot be null. If this were
	  a correct JTD schema, it would be unclear which piece of information
	  takes precedence.</t> 
          <t>JTD handles such possible ambiguity by disallowing, at the
	  syntactic level, the possibility of contradictory specifications of
	  whether an instance described by a schema of the
	  "discriminator" form may be null. The schemas in a
	  discriminator "mapping" cannot have "nullable" set to
	  "true"; only the discriminator itself can use
	  "nullable" in this way.</t> 
          <t>It also follows that</t>
<sourcecode type="json"> 
   {
     "discriminator": "event_type",
     "mapping": {
       "is_event_type_a_string_or_a_float32?": {
         "properties": { "event_type": { "type": "float32" }}
       }
     }
   }
</sourcecode> 
          <t>and</t>
<sourcecode type="json"> 
   {
     "discriminator": "event_type",
     "mapping": {
       "is_event_type_a_string_or_an_optional_float32?": {
         "optionalProperties": { "event_type": { "type": "float32" }}
       }
     }
   }
</sourcecode> 
          <t>are incorrect schemas, as "event_type" is both the value
	  of "discriminator" and a member name in one of the
	  "mapping" member "properties" or
	  "optionalProperties". This is ambiguous, because ordinarily
	  the "discriminator" keyword would indicate that
	  "event_type" is expected to be a string, but another part of
	  the schema specifies that "event_type" is expected to be a
	  number.</t> 
          <t>JTD handles such possible ambiguity by disallowing, at the
	  syntactic level, the possibility of contradictory specifications of
	  discriminator "tags". Discriminator "tags" cannot be redefined in
	  other parts of the schema.</t> 
          <t>By contrast,</t>
<sourcecode type="json"> 
   {
     "discriminator": "event_type",
     "mapping": {
       "account_deleted": {
         "properties": {
           "account_id": { "type": "string" }
         }
       },
       "account_payment_plan_changed": {
         "properties": {
           "account_id": { "type": "string" },
           "payment_plan": { "enum": ["FREE", "PAID"] }
         },
         "optionalProperties": {
           "upgraded_by": { "type": "string" }
         }
       }
     }
   }
</sourcecode> 
          <t>is a correct schema, describing a pattern of data common in
	  JSON-based messaging systems. <xref
	  target="semantics-form-discriminator" format="default"/> provides
	  examples of what this schema accepts and rejects.</t> 
        </section>
      </section>
      <section anchor="extending-JTD-syntax" numbered="true" toc="default">
        <name>Extending JTD's Syntax</name>

        <t>This document does not describe any extension mechanisms for JTD
	schema validation, which is described in <xref target="semantics"
	format="default"/>. However, schemas are defined to optionally contain
	a "metadata" keyword, whose value is an arbitrary JSON
	object. Call the members of this object "metadata members".</t> 
        <t>Users <bcp14>MAY</bcp14> add metadata members to JTD schemas to convey information
	that is not pertinent to validation. For example, such metadata
	members could provide hints to code generators or trigger some
	special behavior for a library that generates user interfaces from
	schemas.</t> 
        <t>Users <bcp14>SHOULD NOT</bcp14> expect metadata members to be understood by other
	parties. As a result, if consistent validation with other parties is a
	requirement, users <bcp14>MUST NOT</bcp14> use metadata members to affect how schema
	validation, as described in <xref target="semantics"
	format="default"/>, works.</t> 
        <t>Users <bcp14>MAY</bcp14> expect metadata members to be understood by other
	parties and <bcp14>MAY</bcp14> use metadata members to affect how schema validation
	works, if these other parties are somehow known to support these
	metadata members. For example, two parties may agree, out of band,
	that they will support an extended JTD with a custom metadata member
	that affects validation.</t> 
      </section>
    </section>
    <section anchor="semantics" numbered="true" toc="default">
      <name>Semantics</name>
      <t>This section describes when an instance is valid against a correct
      JTD schema and the error indicators to produce when an instance is
      invalid.</t> 
      <section anchor="allow-additional-properties" numbered="true" toc="default">
        <name>Allowing Additional            Properties</name>
        <t>Users will have different desired behavior with respect to
	"unspecified" members in an instance. For example, consider the JTD
	schema in <xref target="JTD-properties-a" format="default"/>:</t> 
        <figure anchor="JTD-properties-a">
          <name>An Illustrative JTD Schema</name>
<sourcecode type="json"> 
{ "properties": { "a": { "type": "string" }}}
</sourcecode> 
        </figure>
        <t>Some users may expect that</t>
<sourcecode type="json"> 
   {"a": "foo", "b": "bar"}
</sourcecode> 
        <t>satisfies the schema in <xref target="JTD-properties-a"
	format="default"/>. Others may disagree, as "b" is not one of
	the properties described in the schema. In this document, allowing
	such "unspecified" members, like "b" in this example, happens
	when evaluation is in "allow additional properties" mode.</t>

        <t>Evaluation of a schema does not allow additional properties by
        default, but this can be overridden by having the schema include a
        member named "additionalProperties", where that member has a
        value of "true".</t>
        <t>More formally, evaluation of a schema <em>S</em> is in "allow
	additional properties" mode if there exists a member of <em>S</em>
	whose name equals "additionalProperties" and whose value is a
	boolean "true". Otherwise, evaluation of <em>S</em> is not in
	"allow additional properties" mode.</t> 
        <t>See <xref target="semantics-form-props" format="default"/> for how
	allowing unknown properties affects schema evaluation, but briefly,
	the schema</t> 
<sourcecode type="json"> 
   { "properties": { "a": { "type": "string" }}}
</sourcecode> 
        <t>rejects</t>
<sourcecode type="json"> 
   { "a": "foo", "b": "bar" }
</sourcecode> 
        <t>However, the schema</t>
<sourcecode type="json"> 
   {
     "additionalProperties": true,
     "properties": { "a": { "type": "string" }}
   }
</sourcecode> 
        <t>accepts</t>
<sourcecode type="json"> 
   { "a": "foo", "b": "bar" }
</sourcecode> 
        <t>Note that "additionalProperties" does not get "inherited" by
	subschemas. For example, the JTD schema</t>
<sourcecode type="json"> 
   {
     "additionalProperties": true,
     "properties": {
       "a": {
         "properties": {
           "b": { "type": "string" }
         }
       }
     }
   }
</sourcecode> 
        <t>accepts</t>
<sourcecode type="json"> 
   { "a": { "b": "c" }, "foo": "bar" }
</sourcecode> 
        <t>but rejects</t>
<sourcecode type="json"> 
   { "a": { "b": "c", "foo": "bar" }}
</sourcecode> 
        <t>because the "additionalProperties" at the root level does
	not affect the behavior of subschemas.</t>
        <t>Note from <xref target="cddl-schema" format="default"/> that only
	schemas of the "properties" form may have a member named
	"additionalProperties".</t> 
      </section>
      <section anchor="errors" numbered="true" toc="default">
        <name>Errors</name>
        <t>To facilitate consistent validation error handling, this document
	specifies a standard error indicator format. Implementations <bcp14>SHOULD</bcp14>
	support producing error indicators in this standard form.</t>
        <t>The standard error indicator format is a JSON array. The order of
	the elements of this array is not specified. The elements of this
	array are JSON objects with:</t>
        <ul spacing="normal">
          <li>A member with the name "instancePath", whose value is a
	  JSON string encoding a JSON Pointer. This JSON Pointer will point to
	  the part of the instance that was rejected.</li>
          <li>A member with the name "schemaPath", whose value is a
	  JSON string encoding a JSON Pointer. This JSON Pointer will point to
	  the part of the schema that rejected the instance.</li>
        </ul>
        <t>The values for "instancePath" and "schemaPath" depend
	on the form of the schema and are described in detail in <xref
	target="semantics-forms" format="default"/>.</t> 
      </section>
      <section anchor="semantics-forms" numbered="true" toc="default">
        <name>Forms</name>
        <t>This section describes, for each of the eight JTD schema forms, the
	rules dictating whether an instance is accepted, as well as the error
	indicators to produce when an instance is invalid.</t>
        <t>The forms a correct schema may take on are formally described in
	<xref target="syntax" format="default"/>.</t> 
        <section anchor="semantics-form-empty" numbered="true" toc="default">
          <name>Empty</name>
          <t>The "empty" form is meant to describe instances whose
	  values are unknown, unpredictable, or otherwise unconstrained by the
	  schema. The syntax of the "empty" form is described in <xref
	  target="syntax-form-empty" format="default"/>.</t> 
          <t>If a schema is of the "empty" form, then it accepts all
	  instances. A schema of the "empty" form will never produce any error
	  indicators.</t> 
        </section>
        <section anchor="semantics-form-ref" numbered="true" toc="default">
          <name>Ref</name>
          <t>The "ref" form is for when a schema is defined in terms of
	  something in the "definitions" of the root schema. The "ref"
	  form enables schemas to be less repetitive and also enables
	  describing recursive structures. The syntax of the "ref" form
	  is described in <xref target="syntax-form-ref"
	  format="default"/>.</t> 
          <t>If a schema is of the "ref" form, then:</t>
          <ul spacing="normal">
            <li><t>If the schema has a member named "nullable" whose value
	    is the boolean "true", and the instance is the JSON
	    primitive value "null", then the schema accepts the
	    instance.</t> <t>Otherwise:</t>
<ul>
            <li>Let <em>R</em> be the value of the schema member with the name
	    "ref".</li> 

            <li>Let <em>B</em> be the root schema containing the schema or
	    the schema itself if it is a root schema.</li>
            <li>Let <em>D</em> be the member of <em>B</em> with the name

	    "definitions". Per <xref target="syntax" format="default"/>,
	    we know <em>D</em> exists.</li>

            <li>Let <em>S</em> be the value of the member of <em>D</em> whose
	    name equals <em>R</em>. Per <xref target="syntax-form-ref"
	    format="default"/>, we know <em>S</em> exists and is a schema.</li>
</ul></li>
          </ul>
          <t>The schema accepts the instance if and only if <em>S</em> accepts
	  the instance. Otherwise, the error indicators to return in this case
	  are the union of the error indicators from evaluating <em>S</em>
	  against the instance.</t>
          <t>For example, the schema</t>
<sourcecode type="json"> 
   {
     "definitions": { "a": { "type": "float32" }},
     "ref": "a"
   }
</sourcecode> 
          <t>accepts</t>
<sourcecode type="json"> 
   123
</sourcecode> 
          <t>but rejects</t>
<sourcecode type="json"> 
   null
</sourcecode> 
          <t>with the error indicator</t>
<sourcecode type="json"> 
   [{ "instancePath": "", "schemaPath": "/definitions/a/type" }]
</sourcecode> 
          <t>The schema</t>
<sourcecode type="json"> 
   {
     "definitions": { "a": { "type": "float32" }},
     "ref": "a",
     "nullable": true
   }
</sourcecode> 
          <t>accepts</t>
<sourcecode type="json"> 
   null
</sourcecode> 
<t>because the schema has a "nullable" member whose value is
	  "true".</t> 
          <t>Note that "nullable" being "false" has no effect in
	  any of the forms described in this document. For example, the
	  schema</t> 
<sourcecode type="json"> 
   {
     "definitions": { "a": { "nullable": false, "type": "float32" }},
     "ref": "a",
     "nullable": true
   }
</sourcecode> 
          <t>accepts</t>
<sourcecode type="json"> 
   null
</sourcecode> 
          <t>In other words, it is not the case that putting a "false"
	  value for "nullable" will ever override a "nullable"
	  member in schemas of the "ref" form; it is correct, though
	  ineffectual, to have a value of "false" for the
	  "nullable" member in a schema.</t>
        </section>
        <section anchor="semantics-form-type" numbered="true" toc="default">
          <name>Type</name>
          <t>The "type" form is meant to describe instances whose value
	  is a boolean, number, string, or timestamp <xref target="RFC3339"
	  format="default"/>. The syntax of the "type" form is
	  described in <xref target="syntax-form-type" format="default"/>.</t>



          <t>If a schema is of the "type" form, then:</t>
          <ul spacing="normal">
            <li><t>If the schema has a member named "nullable" whose value
	    is the boolean "true", and the instance is the JSON
	    primitive value "null", then the schema accepts the
	    instance.</t><t>Otherwise:</t> 
<ul empty="true">            
<li>Let <em>T</em> be the value of the member with the name "type". The
following table describes whether the instance is accepted, as a function of
<em>T</em>'s value:</li>
<li>

       <table anchor="type-values" align="center">
            <name>Accepted Values for Type</name>
            <thead>
              <tr>
                <th align="left">If <em>"T"</em> equals ...</th>
                <th align="left">then the instance is accepted if it is ...</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td align="left">boolean</td>
                <td align="left">equal to "true" or "false"</td>
              </tr>
              <tr>
                <td align="left">float32</td>
                <td align="left">a JSON number</td>
              </tr>
              <tr>
                <td align="left">float64</td>
                <td align="left">a JSON number</td>
              </tr>
              <tr>
                <td align="left">int8</td>
                <td align="left">See <xref target="int-ranges" format="default"/></td>
              </tr>
              <tr>
                <td align="left">uint8</td>
                <td align="left">See <xref target="int-ranges" format="default"/></td>
              </tr>
              <tr>
                <td align="left">int16</td>
                <td align="left">See <xref target="int-ranges" format="default"/></td>
              </tr>
              <tr>
                <td align="left">uint16</td>
                <td align="left">See <xref target="int-ranges" format="default"/></td>
              </tr>
              <tr>
                <td align="left">int32</td>
                <td align="left">See <xref target="int-ranges" format="default"/></td>
              </tr>
              <tr>
                <td align="left">uint32</td>
                <td align="left">See <xref target="int-ranges" format="default"/></td>
              </tr>
              <tr>
                <td align="left">string</td>
                <td align="left">a JSON string</td>
              </tr>
              <tr>
                <td align="left">timestamp</td>
                <td align="left">a JSON string that follows the standard
		format described in <xref target="RFC3339" format="default"/>,
		as refined by <xref target="RFC4287"
		sectionFormat="of" section="3.3"/></td> 
              </tr>
            </tbody>
          </table>


          <t>"float32" and "float64" are distinguished from each
	  other in their intent. "float32" indicates data intended to
	  be processed as an IEEE 754 single-precision float, whereas
	  "float64" indicates data intended to be processed as an IEEE
	  754 double-precision float. Tools that generate code from JTD
	  schemas will likely produce different code for "float32" than
	  for "float64".</t>

</li>


</ul></li>
          </ul>
   
          <t>If <em>T</em> starts with "int" or "uint", then the
	  instance is accepted if and only if it is a JSON number encoding a
	  value with zero fractional part. Depending on the value of
	  <em>T</em>, this encoded number must additionally fall within a
	  particular range:</t>
          <table anchor="int-ranges" align="center">
            <name>Ranges for Integer Types</name>
            <thead>
              <tr>
                <th align="left"><em>"T"</em></th>
                <th align="left">Minimum Value (Inclusive)</th>
                <th align="left">Maximum Value (Inclusive)</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td align="left">int8</td>
                <td align="left">-128</td>
                <td align="left">127</td>
              </tr>
              <tr>
                <td align="left">uint8</td>
                <td align="left">0</td>
                <td align="left">255</td>
              </tr>
              <tr>
                <td align="left">int16</td>
                <td align="left">-32,768</td>
                <td align="left">32,767</td>
              </tr>
              <tr>
                <td align="left">uint16</td>
                <td align="left">0</td>
                <td align="left">65,535</td>
              </tr>
              <tr>
                <td align="left">int32</td>
                <td align="left">-2,147,483,648</td>
                <td align="left">2,147,483,647</td>
              </tr>
              <tr>
                <td align="left">uint32</td>
                <td align="left">0</td>
                <td align="left">4,294,967,295</td>
              </tr>
            </tbody>
          </table>
          <t>Note that</t>
<sourcecode type="json"> 
   10
</sourcecode> 
          <t>and</t>
<sourcecode type="json"> 
   10.0
</sourcecode> 
          <t>and</t>
<sourcecode type="json"> 
   1.0e1
</sourcecode> 
          <t>encode values with zero fractional part, whereas</t>
<sourcecode type="json"> 
   10.5
</sourcecode> 
          <t>encodes a number with a non-zero fractional part. Thus, the schema</t>
<sourcecode type="json"> 
   {"type": "int8"}
</sourcecode> 
          <t>accepts</t>
<sourcecode type="json"> 
   10
</sourcecode> 
          <t>and</t>
<sourcecode type="json"> 
   10.0
</sourcecode> 
          <t>and</t>
<sourcecode type="json"> 
   1.0e1
</sourcecode> 
          <t>but rejects</t>
<sourcecode type="json"> 
   10.5
</sourcecode> 
          <t>as well as</t>
<sourcecode type="json"> 
   false
</sourcecode> 
          <t>because "false" is not a number at all.</t>
          <t>If the instance is not accepted, then the error indicator for
	  this case shall have an "instancePath" pointing to the
	  instance and a "schemaPath" pointing to the schema member
	  with the name "type".</t> 
          <t>For example, the schema</t>
<sourcecode type="json"> 
   {"type": "boolean"}
</sourcecode> 
          <t>accepts</t>
<sourcecode type="json"> 
   false
</sourcecode> 
          <t>but rejects</t>
<sourcecode type="json"> 
   127
</sourcecode> 
          <t>The schema</t>
<sourcecode type="json"> 
   {"type": "float32"}
</sourcecode> 
          <t>accepts</t>
<sourcecode type="json"> 
   10.5
</sourcecode> 
          <t>and</t>
<sourcecode type="json"> 
   127
</sourcecode> 
          <t>but rejects</t>
<sourcecode type="json"> 
   false
</sourcecode> 
          <t>The schema</t>
<sourcecode type="json"> 
   {"type": "string"}
</sourcecode> 
          <t>accepts</t>
<sourcecode type="json"> 
   "1985-04-12T23:20:50.52Z"
</sourcecode> 
          <t>and</t>
<sourcecode type="json"> 
   "foo"
</sourcecode> 
          <t>but rejects</t>
<sourcecode type="json"> 
   false
</sourcecode> 
          <t>The schema</t>
<sourcecode type="json"> 
   {"type": "timestamp"}
</sourcecode> 
          <t>accepts</t>
<sourcecode type="json"> 
   "1985-04-12T23:20:50.52Z"
</sourcecode> 
          <t>but rejects</t>
<sourcecode type="json"> 
   "foo"
</sourcecode> 
          <t>and</t>
<sourcecode type="json"> 
   false
</sourcecode> 
          <t>The schema</t>
<sourcecode type="json"> 
   {"type": "boolean", "nullable": true}
</sourcecode> 
          <t>accepts</t>
<sourcecode type="json"> 
   null
</sourcecode> 
          <t>and</t>
<sourcecode type="json"> 
   false
</sourcecode> 
          <t>but rejects</t>
<sourcecode type="json"> 
   127
</sourcecode> 

          <t>In all of the examples of rejected instances given in this
	  section, the error indicator to produce is:</t>
<sourcecode type="json"> 
   [{ "instancePath": "", "schemaPath": "/type" }]
</sourcecode> 
        </section>
        <section anchor="semantics-form-enum" numbered="true" toc="default">
          <name>Enum</name>
          <t>The "enum" form is meant to describe instances whose value
	  must be one of a given set of string values. The syntax of the
	  "enum" form is described in <xref target="syntax-form-enum"
	  format="default"/>.</t> 
          <t>If a schema is of the "enum" form, then:</t>
          <ul spacing="normal">
            <li><t>If the schema has a member named "nullable" whose value
	    is the boolean "true", and the instance is the JSON
	    primitive value "null", then the schema accepts the
	    instance.</t> <t>Otherwise:</t> 
<ul empty="true">            
<li>Let <em>E</em> be the value of the schema member with the name "enum". The
instance is accepted if and only if it is equal to one of the elements of
<em>E</em>.
</li>  
         </ul>
</li>
</ul>
          <t>If the instance is not accepted, then the error indicator for
	  this case shall have an "instancePath" pointing to the
	  instance and a "schemaPath" pointing to the schema member
	  with the name "enum".</t> 
          <t>For example, the schema</t>
<sourcecode type="json"> 
   { "enum": ["PENDING", "DONE", "CANCELED"] }
</sourcecode> 
          <t>accepts</t>
<sourcecode type="json"> 
   "PENDING"
</sourcecode> 
          <t>and</t>
<sourcecode type="json"> 
   "DONE"
</sourcecode> 
          <t>and</t>
<sourcecode type="json"> 
   "CANCELED"
</sourcecode> 
          <t>but rejects all of</t>
<sourcecode type="json"> 
   0
</sourcecode> 
          <t>and</t>
<sourcecode type="json"> 
   1
</sourcecode> 
          <t>and</t>
<sourcecode type="json"> 
   2
</sourcecode> 
          <t>and</t>
<sourcecode type="json"> 
   "UNKNOWN"
</sourcecode> 
          <t>and</t>
<sourcecode type="json"> 
   null
</sourcecode> 
          <t>with the error indicator</t>
<sourcecode type="json"> 
   [{ "instancePath": "", "schemaPath": "/enum" }]
</sourcecode> 
          <t>The schema</t>
<sourcecode type="json"> 
   { "enum": ["PENDING", "DONE", "CANCELED"], "nullable": true }
</sourcecode> 
          <t>accepts</t>
<sourcecode type="json"> 
   "PENDING"
</sourcecode> 
          <t>and</t>
<sourcecode type="json"> 
   null
</sourcecode> 
          <t>but rejects</t>
<sourcecode type="json"> 
   1
</sourcecode> 
          <t>and</t>
<sourcecode type="json"> 
   "UNKNOWN"
</sourcecode> 
          <t>with the error indicator</t>
<sourcecode type="json"> 
   [{ "instancePath": "", "schemaPath": "/enum" }]
</sourcecode> 
        </section>
        <section anchor="semantics-form-elements" numbered="true" toc="default">
          <name>Elements</name>
          <t>The "elements" form is meant to describe instances that
	  must be arrays. A further subschema describes the elements of the
	  array. The syntax of the "elements" form is described in
	  <xref target="syntax-form-elements" format="default"/>.</t> 
          <t>If a schema is of the "elements" form, then:</t>
          <ul spacing="normal">
            <li><t>If the schema has a member named "nullable" whose value
	    is the boolean "true", and the instance is the JSON
	    primitive value "null", then the schema accepts the
	    instance.</t> <t>Otherwise:</t> 
          
  <ul empty="true"><li>
              <t>Let <em>S</em> be the value of the schema member with the
	      name "elements". The instance is accepted if and only if
	      all of the following are true:  </t></li>
<li> 
              <ul spacing="normal">
                <li>The instance is an array. Otherwise, the error indicator
		for this case shall have an "instancePath" pointing to
		the instance and a "schemaPath" pointing to the schema
		member with the name "elements".</li> 
                <li>If the instance is an array, then every element of the
		instance must be accepted by <em>S</em>. Otherwise, the error
		indicators for this case are the union of all the errors
		arising from evaluating <em>S</em> against elements of the
		instance.</li> 
              </ul>
</li></ul>
            </li>
          </ul>
          <t>For example, the schema</t>
<sourcecode type="json"> 
   {
     "elements": {
       "type": "float32"
     }
   }
</sourcecode> 
          <t>accepts</t>
<sourcecode type="json"> 
   []
</sourcecode> 
          <t>and</t>
<sourcecode type="json">    
   [1, 2, 3]
</sourcecode> 
          <t>but rejects</t>
<sourcecode type="json"> 
   null
</sourcecode> 
          <t>with the error indicator</t>
<sourcecode type="json"> 
   [{ "instancePath": "", "schemaPath": "/elements" }]
</sourcecode> 
          <t>and rejects</t>
<sourcecode type="json"> 
   [1, 2, "foo", 3, "bar"]
</sourcecode> 
          <t>with the error indicators</t>
<sourcecode type="json"> 
   [
     { "instancePath": "/2", "schemaPath": "/elements/type" },
     { "instancePath": "/4", "schemaPath": "/elements/type" }
   ]
</sourcecode> 
          <t>The schema</t>
<sourcecode type="json"> 
   {
     "elements": {
       "type": "float32"
     },
     "nullable": true
   }
</sourcecode> 
          <t>accepts</t>
<sourcecode type="json"> 
   null
</sourcecode> 
          <t>and</t>
<sourcecode type="json"> 
   []
</sourcecode> 
          <t>and</t>
<sourcecode type="json"> 
   [1, 2, 3]
</sourcecode> 
          <t>but rejects</t>
<sourcecode type="json"> 
   [1, 2, "foo", 3, "bar"]
</sourcecode> 
          <t>with the error indicators</t>
<sourcecode type="json"> 
   [
     { "instancePath": "/2", "schemaPath": "/elements/type" },
     { "instancePath": "/4", "schemaPath": "/elements/type" }
   ]
</sourcecode> 
        </section>



        <section anchor="semantics-form-props" numbered="true" toc="default">
          <name>Properties</name>
          <t>The "properties" form is meant to describe JSON objects
	  being used as a "struct". The syntax of the "properties" form
	  is described in <xref target="syntax-form-properties"
	  format="default"/>.</t> 
          <t>If a schema is of the "properties" form, then:</t>
          <ul spacing="normal">
            <li><t>If the schema has a member named "nullable" whose value
	    is the boolean "true", and the instance is the JSON
	    primitive value "null", then the schema accepts the
	    instance.</t> <t>Otherwise:</t>
            
<ul><li>
              <t>The instance must be an object.</t>
              <t>Otherwise, the schema rejects the instance. The error indicator for this
                 case shall have an "instancePath" pointing to the instance,
                 and a "schemaPath" pointing to the schema member with the
                 name "properties" if such a schema member exists; if such a
                 member doesn't exist, "schemaPath" shall point to the schema
                 member with the name "optionalProperties".</t>
</li>       
     
            <li>
              <t>If the instance is an object, and the schema has a member
              named "properties", then let <em>P</em> be the value of the
              schema member named "properties". Per <xref
              target="syntax-form-properties" format="default"/>, we know
              <em>P</em> is an object. For every member name in <em>P</em>, a
              member of the same name in the instance must exist.  </t>



<t> Otherwise, the schema rejects the instance. The error indicator for this
case shall have an "instancePath" pointing to the instance, and a "schemaPath"
pointing to the member of <em>P</em> failing the requirement just described.
</t>



            </li>
            <li>
              <t>If the instance is an object, then let <em>P</em> be the
	      value of the schema member named "properties" (if it
	      exists) and <em>O</em> be the value of the schema member named
	      "optionalProperties" (if it exists).  </t> 
              <t>For every member <em>I</em> of the instance, find a
	      member with the same name as <em>I</em>'s in <em>P</em> or
	      <em>O</em>. Per <xref target="syntax-form-properties"
	      format="default"/>, we know it is not possible for both <em>P</em> and
	      <em>O</em> to have such a member. If the "discriminator tag
	      exemption" is in effect on <em>I</em> (see <xref
	      target="semantics-form-discriminator" format="default"/>), then
	      ignore <em>I</em>.</t><t>Otherwise:  </t>
              <ul spacing="normal">
                <li>
                  <t>If no such member in <em>P</em> or <em>O</em> exists and
                  validation is not in "allow additional properties" mode (see
                  <xref target="allow-additional-properties"
                  format="default"/>), then the schema rejects the instance.
                  </t>
                  <t>The error indicator for this case has an
		  "instancePath" pointing to <em>I</em> and a
		  "schemaPath" pointing to the schema.</t> 
                </li>
                <li>
                  <t>If such a member in <em>P</em> or <em>O</em> does exist,
		  then call this member <em>S</em>. If <em>S</em> rejects
		  <em>I</em>'s value, then the schema rejects the instance.</t>
                  <t>The error indicators for this case are the union of the
		  error indicators from evaluating <em>S</em> against <em>I</em>'s value.</t>
                </li>
              </ul>
            </li>
          </ul>
</li>
	  </ul>
<ul empty="true">
<li>
          <t>If an instance is an object, it may have multiple errors arising
	  from the second and third bullet in the list above. In this case,
	  the error indicators are the union of the errors.</t> 
          <t>For example, the schema</t>

<sourcecode type="json"> 
   {
     "properties": {
       "a": { "type": "string" },
       "b": { "type": "string" }
     },
     "optionalProperties": {
       "c": { "type": "string" },
       "d": { "type": "string" }
     }
   }
</sourcecode> 
          <t>accepts</t>
<sourcecode type="json"> 
   { "a": "foo", "b": "bar" }
</sourcecode> 
          <t>and</t>
<sourcecode type="json">  
   { "a": "foo", "b": "bar", "c": "baz" }
</sourcecode> 
          <t>and</t>
<sourcecode type="json"> 
   { "a": "foo", "b": "bar", "c": "baz", "d": "quux" }
</sourcecode> 
          <t>and</t>
<sourcecode type="json"> 
   { "a": "foo", "b": "bar", "d": "quux" }
</sourcecode> 
          <t>but rejects</t>
<sourcecode type="json"> 
   null
</sourcecode> 
          <t>with the error indicator</t>
<sourcecode type="json"> 
   [{ "instancePath": "", "schemaPath": "/properties" }]
</sourcecode> 
          <t>and rejects</t>
<sourcecode type="json"> 
   { "b": 3, "c": 3, "e": 3 }
</sourcecode> 
          <t>with the error indicators</t>
<sourcecode type="json"> 
   [
     { "instancePath": "",
       "schemaPath": "/properties/a" },
     { "instancePath": "/b",
       "schemaPath": "/properties/b/type" },
     { "instancePath": "/c",
       "schemaPath": "/optionalProperties/c/type" },
     { "instancePath": "/e",
       "schemaPath": "" }
   ]
</sourcecode> 
          <t>If instead the schema had "additionalProperties: true"
	  but was otherwise the same:</t> 
<sourcecode type="json"> 
   {
     "properties": {
       "a": { "type": "string" },
       "b": { "type": "string" }
     },
     "optionalProperties": {
       "c": { "type": "string" },
       "d": { "type": "string" }
     },
     "additionalProperties": true
   }
</sourcecode> 
          <t>and the instance remained the same:</t>
<sourcecode type="json"> 
   { "b": 3, "c": 3, "e": 3 }
</sourcecode> 
          <t>then the error indicators from evaluating the instance against
	  the schema would be:</t>
<sourcecode type="json"> 
   [
     { "instancePath": "",
       "schemaPath": "/properties/a" },
     { "instancePath": "/b",
       "schemaPath": "/properties/b/type" },
     { "instancePath": "/c",
       "schemaPath": "/optionalProperties/c/type" },
   ]
</sourcecode> 
          <t>These are the same errors as before, except the final error
	  (associated with the additional member named "e" in the
	  instance) is no longer present. This is because
	  "additionalProperties: true" enables "allow additional
	  properties" mode on the schema.</t>
          <t>Finally, the schema</t>
<sourcecode type="json"> 
   {
     "nullable": true,
     "properties": {
       "a": { "type": "string" },
       "b": { "type": "string" }
     },
     "optionalProperties": {
       "c": { "type": "string" },
       "d": { "type": "string" }
     },
     "additionalProperties": true
   }
</sourcecode> 
          <t>accepts</t>
<sourcecode type="json"> 
   null
</sourcecode> 
          <t>but rejects</t>
<sourcecode type="json"> 
   { "b": 3, "c": 3, "e": 3 }
</sourcecode> 
          <t>with the error indicators</t>
<sourcecode type="json"> 
   [
     { "instancePath": "",
       "schemaPath": "/properties/a" },
     { "instancePath": "/b",
       "schemaPath": "/properties/b/type" },
     { "instancePath": "/c",
       "schemaPath": "/optionalProperties/c/type" },
   ]
</sourcecode> 
</li>
</ul>
        </section>
        <section anchor="semantics-form-values" numbered="true" toc="default">
          <name>Values</name>
          <t>The "values" form is meant to describe instances that are
	  JSON objects being used as an associative array. The syntax of the
	  "values" form is described in <xref
	  target="syntax-form-values" format="default"/>.</t> 
          <t>If a schema is of the "values" form, then:</t>
          <ul spacing="normal">
            <li><t>If the schema has a member named "nullable" whose value
	    is the boolean "true", and the instance is the JSON
	    primitive value "null", then the schema accepts the
	    instance.</t> <t>Otherwise:</t> 
            
<ul empty="true">
<li>
              <t>Let <em>S</em> be the value of the schema member with the
	      name "values". The instance is accepted if and only if
	      all of the following are true:  </t> 
              <ul spacing="normal">
                <li>The instance is an object. Otherwise, the error indicator
		for this case shall have an "instancePath" pointing to
		the instance and a "schemaPath" pointing to the schema
		member with the name "values".</li> 
                <li>If the instance is an object, then every member value of
		the instance must be accepted by <em>S</em>. Otherwise, the
		error indicators for this case are the union of all the error
		indicators arising from evaluating <em>S</em> against member
		values of the instance.</li> 
              </ul>
            </li>
          </ul>
</li>
	  </ul>
          <t>For example, the schema</t>
<sourcecode type="json"> 
   {
     "values": {
       "type": "float32"
     }
   }
</sourcecode> 
          <t>accepts</t>
<sourcecode type="json"> 
   {}
</sourcecode> 
          <t>and</t>
<sourcecode type="json"> 
   {"a": 1, "b": 2}
</sourcecode> 
          <t>but rejects</t>
<sourcecode type="json"> 
   null
</sourcecode> 
          <t>with the error indicator</t>
<sourcecode type="json"> 
   [{ "instancePath": "", "schemaPath": "/values" }]
</sourcecode> 
          <t>and rejects</t>
<sourcecode type="json"> 
   { "a": 1, "b": 2, "c": "foo", "d": 3, "e": "bar" }
</sourcecode> 
          <t>with the error indicators</t>
<sourcecode type="json"> 
   [
     { "instancePath": "/c", "schemaPath": "/values/type" },
     { "instancePath": "/e", "schemaPath": "/values/type" }
   ]
</sourcecode> 
          <t>The schema</t>
<sourcecode type="json"> 
   {
     "nullable": true,
     "values": {
       "type": "float32"
     }
   }
</sourcecode> 
          <t>accepts</t>
<sourcecode type="json"> 
   null
</sourcecode> 
          <t>but rejects</t>
<sourcecode type="json"> 
   { "a": 1, "b": 2, "c": "foo", "d": 3, "e": "bar" }
</sourcecode> 
          <t>with the error indicators</t>
<sourcecode type="json"> 
   [
     { "instancePath": "/c", "schemaPath": "/values/type" },
     { "instancePath": "/e", "schemaPath": "/values/type" }
   ]
</sourcecode> 
        </section>
        <section anchor="semantics-form-discriminator" numbered="true" toc="default">
          <name>Discriminator</name>
          <t>The "discriminator" form is meant to describe JSON objects
	  being used in a fashion similar to a discriminated union construct
	  in C-like languages. The syntax of the "discriminator" form
	  is described in <xref target="syntax-form-discriminator"
	  format="default"/>.</t> 
          <t>When a schema is of the "discriminator" form, it validates that:</t>
          <ul spacing="normal">
            <li>the instance is an object,</li>
            <li>the instance has a particular "tag" property,</li>
            <li>this "tag" property's value is a string within a set of
	    valid values, and</li> 
            <li>the instance satisfies another schema, where this other
	    schema is chosen based on the value of the "tag" property.</li>
          </ul>
          <t>The behavior of the "discriminator" form is more complex than the
	  other keywords. Readers familiar with CDDL may find the final
	  example in <xref target="comparison-with-cddl" format="default"/>
	  helpful in understanding its behavior. What follows in this section
	  is a description of the "discriminator" form's behavior, as well as
	  some examples.</t> 
          <t>If a schema is of the "discriminator" form, then:</t>
          <ul spacing="normal">
            <li>Let <em>D</em> be the schema member with the name
	    "discriminator".</li> 
            <li>Let <em>M</em> be the schema member with the name
	    "mapping".</li> 
            <li>Let <em>I</em> be the instance member whose name equals
	    <em>D</em>'s value. <em>I</em> may, for some rejected instances,
	    not exist.</li>  
            <li>Let <em>S</em> be the member of <em>M</em> whose name equals
	    <em>I</em>'s value. <em>S</em> may, for some rejected instances,
	    not exist.</li> 
          </ul>
          <t>If the schema has a member named "nullable" whose value is
	  the boolean "true", and the instance is the JSON primitive
	  value "null", then the schema accepts the instance. Otherwise,
	  the instance is accepted if and only if all of the following are
	  true:</t> 
          <ul spacing="normal">
            <li>
              <t>The instance is an object.  </t>
              <t>Otherwise, the error indicator for this case shall have an
	      "instancePath" pointing to the instance and a
	      "schemaPath" pointing to <em>D</em>.</t>
            </li>
            <li>
              <t>If the instance is a JSON object, then <em>I</em> must exist.  </t>
              <t>Otherwise, the error indicator for this case shall have an
	      "instancePath" pointing to the instance and a
	      "schemaPath" pointing to <em>D</em>.</t>
            </li>
            <li>
              <t>If the instance is a JSON object and <em>I</em> exists,
	      <em>I</em>'s value must be a string.  </t> 
              <t>Otherwise, the error indicator for this case shall have an
	      "instancePath" pointing to <em>I</em> and a
	      "schemaPath" pointing to <em>D</em>.</t> 
            </li>
            <li>
              <t>If the instance is a JSON object and <em>I</em> exists and
	      has a string value, then <em>S</em> must exist.  </t> 
              <t>Otherwise, the error indicator for this case shall have an
	      "instancePath" pointing to <em>I</em> and a
	      "schemaPath" pointing to <em>M</em>.</t> 
            </li>
            <li>
              <t>If the instance is a JSON object, <em>I</em> exists, and
	      <em>S</em> exists, then the instance must satisfy <em>S</em>'s
	      value. Per <xref target="syntax" format="default"/>, we know <em>S</em>'s
	      value is a schema of the "properties" form. Apply the
	      "discriminator tag exemption" afforded in <xref
	      target="semantics-form-props" format="default"/> to <em>I</em>
	      when evaluating whether the instance satisfies <em>S</em>'s
	      value.  </t> 
              <t>Otherwise, the error indicators for this case shall be error
	      indicators from evaluating <em>S</em>'s value against the
	      instance, with the "discriminator tag exemption" applied to
	      <em>I</em>.</t> 
            </li>
          </ul>
          <t>The list items above are defined in a mutually exclusive way. For
	  any given instance and schema, exactly one of the list items above
	  will apply.</t> 
          <t>For example, the schema</t>
<sourcecode type="json"> 
   {
     "discriminator": "version",
     "mapping": {
       "v1": {
         "properties": {
           "a": { "type": "float32" }
         }
       },
       "v2": {
         "properties": {
           "a": { "type": "string" }
         }
       }
     }
   }
</sourcecode> 
          <t>rejects</t>
<sourcecode type="json"> 
   null
</sourcecode> 
          <t>with the error indicator</t>
<sourcecode type="json"> 
   [{ "instancePath": "", "schemaPath": "/discriminator" }]
</sourcecode> 
          <t>(This is the case of the instance not being an object.)</t>
          <t>Also rejected is</t>
<sourcecode type="json"> 
   {}
</sourcecode> 
          <t>with the error indicator</t>
<sourcecode type="json"> 
   [{ "instancePath": "", "schemaPath": "/discriminator" }]
</sourcecode> 
          <t>(This is the case of <em>I</em> not existing.)</t>
          <t>Also rejected is</t>
<sourcecode type="json"> 
   { "version": 1 }
</sourcecode> 
          <t>with the error indicator</t>
<sourcecode type="json"> 
   [
     {
       "instancePath": "/version",
       "schemaPath": "/discriminator"
     }
   ]
</sourcecode> 
          <t>(This is the case of <em>I</em> existing but not having a string
	  value.)</t> 
          <t>Also rejected is</t>
<sourcecode type="json"> 
   { "version": "v3" }
</sourcecode> 
          <t>with the error indicator</t>
<sourcecode type="json"> 
   [
     {
       "instancePath": "/version",
       "schemaPath": "/mapping"
     }
   ]
</sourcecode> 
          <t>(This is the case of <em>I</em> existing and having a string
	  value but <em>S</em> not existing.)</t>
          <t>Also rejected is</t>
<sourcecode type="json"> 
   { "version": "v2", "a": 3 }
</sourcecode> 
          <t>with the error indicator</t>
<sourcecode type="json"> 
   [
     {
       "instancePath": "/a",
       "schemaPath": "/mapping/v2/properties/a/type"
     }
   ]
</sourcecode> 
          <t>(This is the case of <em>I</em> and <em>S</em> existing but the
	  instance not satisfying <em>S</em>'s value.)</t>
          <t>Finally, the schema accepts</t>
<sourcecode type="json"> 
   { "version": "v2", "a": "foo" }
</sourcecode> 
          <t>This instance is accepted even though "version" is not
	  mentioned by "/mapping/v2/properties"; the "discriminator tag
	  exemption" ensures that "version" is not treated as an
	  additional property when evaluating the instance against <em>S</em>'s value.</t>
          <t>By contrast, consider the same schema but with "nullable"
	  being "true". The schema</t>
<sourcecode type="json"> 
   {
     "nullable": true,
      "discriminator": "version",
      "mapping": {
        "v1": {
          "properties": {
            "a": { "type": "float32" }
          }
        },
        "v2": {
          "properties": {
            "a": { "type": "string" }
          }
        }
      }
   }
</sourcecode> 
          <t>accepts</t>
<sourcecode type="json"> 
   null
</sourcecode> 
          <t>To further illustrate the "discriminator" form with examples,
	  recall the JTD schema in <xref target="syntax-form-discriminator"
	  format="default"/>, reproduced here:</t> 
<sourcecode type="json"> 
   {
     "discriminator": "event_type",
     "mapping": {
       "account_deleted": {
         "properties": {
           "account_id": { "type": "string" }
         }
       },
       "account_payment_plan_changed": {
         "properties": {
           "account_id": { "type": "string" },
           "payment_plan": { "enum": ["FREE", "PAID"] }
         },
         "optionalProperties": {
           "upgraded_by": { "type": "string" }
         }
       }
     }
   }
</sourcecode> 
          <t>This schema accepts</t>
<sourcecode type="json"> 
   { "event_type": "account_deleted", "account_id": "abc-123" }
</sourcecode> 
          <t>and</t>
<sourcecode type="json"> 
   {
     "event_type": "account_payment_plan_changed",
     "account_id": "abc-123",
     "payment_plan": "PAID"
   }
</sourcecode> 
          <t>and</t>
<sourcecode type="json"> 
   {
     "event_type": "account_payment_plan_changed",
     "account_id": "abc-123",
     "payment_plan": "PAID",
     "upgraded_by": "users/mkhwarizmi"
   }
</sourcecode> 
          <t>but rejects</t>
<sourcecode type="json"> 
   {}
</sourcecode> 
          <t>with the error indicator</t>
<sourcecode type="json"> 
   [{ "instancePath": "", "schemaPath": "/discriminator" }]
</sourcecode> 
          <t>and rejects</t>
<sourcecode type="json"> 
   { "event_type": "some_other_event_type" }
</sourcecode> 
          <t>with the error indicator</t>
<sourcecode type="json"> 
   [
     {
       "instancePath": "/event_type",
       "schemaPath": "/mapping"
     }
   ]
</sourcecode> 
          <t>and rejects</t>
<sourcecode type="json"> 
   { "event_type": "account_deleted" }
</sourcecode> 
          <t>with the error indicator</t>
<sourcecode type="json"> 
   [{
     "instancePath": "",
     "schemaPath": "/mapping/account_deleted/properties/account_id"
   }]
</sourcecode> 
          <t>and rejects</t>
<sourcecode type="json"> 
   {
     "event_type": "account_payment_plan_changed",
     "account_id": "abc-123",
     "payment_plan": "PAID",
     "xxx": "asdf"
   }
</sourcecode> 
          <t>with the error indicator</t>
<sourcecode type="json"> 
   [{
     "instancePath": "/xxx",
     "schemaPath": "/mapping/account_payment_plan_changed"
   }]
</sourcecode> 
        </section>
      </section>
    </section>
    <section anchor="iana-considerations" numbered="true" toc="default">
      <name>IANA Considerations</name>
      <t>This document has no IANA actions.</t>
    </section>
    <section anchor="security-considerations" numbered="true" toc="default">
      <name>Security Considerations</name>
      <t>Implementations of JTD will necessarily be manipulating JSON
      data. Therefore, the security considerations of <xref target="RFC8259"
      format="default"/> are all relevant here.</t>
      <t>Implementations that evaluate user-inputted schemas <bcp14>SHOULD</bcp14> implement
      mechanisms to detect and abort circular references that might cause a
      naive implementation to go into an infinite loop. Without such
      mechanisms, implementations may be vulnerable to denial-of-service
      attacks.</t> 
    </section>
  </middle>
  <back>
<displayreference target="I-D.handrews-json-schema" to="JSON-SCHEMA"/>
    <references>
      <name>References</name>
      <references>
        <name>Normative References</name>
        <xi:include href="https://xml2rfc.ietf.org/public/rfc/bibxml/reference.RFC.3339.xml"/>
        <xi:include href="https://xml2rfc.ietf.org/public/rfc/bibxml/reference.RFC.4287.xml"/>
        <xi:include href="https://xml2rfc.ietf.org/public/rfc/bibxml/reference.RFC.6901.xml"/>
        <xi:include href="https://xml2rfc.ietf.org/public/rfc/bibxml/reference.RFC.8259.xml"/>
        <xi:include href="https://xml2rfc.ietf.org/public/rfc/bibxml/reference.RFC.8610.xml"/>
        <xi:include href="https://xml2rfc.ietf.org/public/rfc/bibxml/reference.RFC.2119.xml"/>
        <xi:include href="https://xml2rfc.ietf.org/public/rfc/bibxml/reference.RFC.8174.xml"/>
      </references>
      <references>
        <name>Informative References</name>
        <xi:include href="https://xml2rfc.ietf.org/public/rfc/bibxml/reference.RFC.7071.xml"/>
        <xi:include href="https://xml2rfc.ietf.org/public/rfc/bibxml/reference.RFC.7493.xml"/>



        <xi:include
	    href="https://datatracker.ietf.org/doc/bibxml3/reference.I-D.handrews-json-schema.xml"/>

        <reference anchor="OPENAPI" target="https://spec.openapis.org/oas/v3.0.3">
          <front>
            <title>OpenAPI Specification</title>
            <author>
              <organization>OpenAPI Initiative</organization>
            </author>
            <date year="2020" month="February" />
          </front>
        </reference>
      </references>
    </references>
    <section anchor="other-considerations" numbered="true" toc="default">
      <name>Rationale for Omitted Features</name>
      <t>This appendix is not normative.</t>
      <t>This section describes possible features that are intentionally left
      out of JSON Type Definition and justifies why these features are
      omitted.</t> 

      <section anchor="other-considerations-int64" numbered="true" toc="default">
        <name>Support for 64-Bit Numbers</name>
        <t>This document does not allow "int64" or "uint64" as
	values for the JTD "type" keyword (see Sections <xref
	target="syntax-form-type" format="counter"/> and <xref
	target="semantics-form-type" format="counter"/>). Such hypothetical
	"int64" or "uint64" types would behave like
	"int32" or "uint32" (respectively) but with the range
	of values associated with 64-bit instead of 32-bit integers. That is:</t>
        <ul spacing="normal">
          <li>
            "int64" would accept numbers between -(2**63) and
	  (2**63)-1</li> 
          <li>
            "uint64" would accept numbers between 0 and (2**64)-1</li> 
        </ul>
        <t>Users of "int64" and "uint64" would likely expect
	that the full range of signed or unsigned 64-bit integers could
	interoperably be transmitted as JSON without loss of precision. But
	this assumption is likely to be incorrect, for the reasons given in
	<xref target="RFC7493" sectionFormat="of" section="2.2"/>.</t> 
        <t>"int64" and "uint64" likely would have led users to
	falsely assume that the full range of 64-bit integers can be
	interoperably processed as JSON without loss of precision. To avoid
	leading users astray, JTD omits "int64" and
	"uint64".</t> 
      </section>
      <section anchor="support-for-non-root-definitions" numbered="true" toc="default">
        <name>Support for Non-root Definitions</name>
        <t>This document disallows the "definitions" keyword from
	appearing outside of root schemas (see <xref target="cddl-schema"
	format="default"/>). Conceivably, this document could have instead
	allowed "definitions" to appear on any schema, even non-root
	ones. Under this alternative design, "ref"s would resolve to a
	definition in the "nearest" (i.e., most nested) schema that both
	contained the "ref" and had a suitably named
	"definitions" member.</t> 
        <t>For instance, under this alternative approach, one could define
	schemas like the one in <xref target="hypothetical-ref"
	format="default"/>.</t> 
        <figure anchor="hypothetical-ref">
          <name>A Hypothetical Schema Had This Document Permitted Non-root
	  Definitions. This Is Not a Correct JTD Schema.</name> 
<sourcecode type="json"> 
{
  "properties": {
    "foo": {
      "definitions": {
        "user": { "properties": { "user_id": {"type": "string" }}}
      },
      "ref": "user"
    },
    "bar": {
      "definitions": {
        "user": { "properties": { "user_id": {"type": "string" }}}
      },
      "ref": "user"
    },
    "baz": {
      "definitions": {
        "user": { "properties": { "userId": {"type": "string" }}}
      },
      "ref": "user"
    }
  }
}
</sourcecode> 
        </figure>
        <t>If schemas like that in <xref target="hypothetical-ref"
	format="default"/> were permitted, code generation from JTD schemas
	would be more difficult, and the generated code would be less useful.</t>
        <t>Code generation would be more difficult because it would force code
	generators to implement a name-mangling scheme for types generated
	from definitions. This additional difficulty is not immense, but it adds
	complexity to an otherwise relatively trivial task.</t>
        <t>Generated code would be less useful because generated, mangled
	struct names are less pithy than human-defined struct names. For
	instance, the "user" definitions in <xref
	target="hypothetical-ref" format="default"/> might have been generated
	into types named "PropertiesFooUser",
	"PropertiesBarUser", and "PropertiesBazUser"; obtuse
	names like these are less useful to human-written code than names like
	"User".</t> 
        <t>Furthermore, even though "PropertiesFooUser" and
	"PropertiesBarUser" would be essentially identical, they would
	not be interchangeable in many statically typed programming
	languages. A code generator could attempt to circumvent this by
	deduplicating identical definitions, but then the user might be
	confused as to why the subtly distinct "PropertiesBazUser",
	defined from a schema allowing a property named "userId" (not
	"user_id"), was not deduplicated.</t> 
        <t>Because there seem to be implementation and usability challenges
	associated with non-root definitions, and because it would be easier
	to later amend JTD to permit for non-root definitions than to later
	amend JTD to prohibit them, this document does not permit non-root
	definitions in JTD schemas.</t> 
      </section>
    </section>
    <section anchor="comparison-with-cddl" numbered="true" toc="default">
      <name>Comparison with CDDL</name>
      <t>This appendix is not normative.</t>
      <t>To aid the reader familiar with CDDL, this section illustrates how
      JTD works by presenting JTD schemas and CDDL schemas that accept and
      reject the same instances.</t>
      <t>The JTD schema</t>
<sourcecode type="json"> 
   {}
</sourcecode> 
      <t>accepts the same instances as the CDDL rule</t>
<sourcecode type="cddl"> 
   root = any
</sourcecode> 
      <t>The JTD schema</t>
<sourcecode type="json"> 
   {
     "definitions": {
       "a": { "elements": { "ref": "b" }},
       "b": { "type": "float32" }
     },
     "elements": {
       "ref": "a"
     }
   }
</sourcecode> <t>accepts the same instances as the CDDL rule</t>
<sourcecode type="cddl"> 
   root = [* a]
   a = [* b]
   b = number
</sourcecode> 
      <t>The JTD schema</t>
<sourcecode type="json"> 
   { "enum": ["PENDING", "DONE", "CANCELED"]}
</sourcecode> 
      <t>accepts the same instances as the CDDL rule</t>
<sourcecode type="cddl"> 
   root = "PENDING" / "DONE" / "CANCELED"
</sourcecode> 
      <t>The JTD schema</t>
<sourcecode type="json"> 
   {"type": "boolean"}
</sourcecode> 
      <t>accepts the same instances as the CDDL rule</t>
<sourcecode type="cddl"> 
   root = bool
</sourcecode> 
      <t>The JTD schemas:</t>
<sourcecode type="json"> 
   {"type": "float32"}
</sourcecode> 
      <t>and</t>
<sourcecode type="json"> 
   {"type": "float64"}
</sourcecode> 
      <t>both accept the same instances as the CDDL rule</t>
<sourcecode type="cddl"> 
   root = number
</sourcecode> 
      <t>The JTD schema</t>
<sourcecode type="json"> 
   {"type": "string"}
</sourcecode> 
      <t>accepts the same instances as the CDDL rule</t>
<sourcecode type="cddl">  
   root = tstr
</sourcecode> 
      <t>The JTD schema</t>
<sourcecode type="json"> 
   {"type": "timestamp"}
</sourcecode> 
      <t>accepts the same instances as the CDDL rule</t>
<sourcecode type="cddl"> 
   root = tdate
</sourcecode> 
      <t>The JTD schema</t>
<sourcecode type="json"> 
   { "elements": { "type": "float32" }}
</sourcecode> 
      <t>accepts the same instances as the CDDL rule</t>
<sourcecode type="cddl"> 
   root = [* number]
</sourcecode> 
      <t>The JTD schema</t>
<sourcecode type="json"> 
   {
     "properties": {
       "a": { "type": "boolean" },
       "b": { "type": "float32" }
     },
     "optionalProperties": {
       "c": { "type": "string" },
       "d": { "type": "timestamp" }
     }
   }
</sourcecode> 
      <t>accepts the same instances as the CDDL rule</t>
<sourcecode type="cddl"> 
   root = { a: bool, b: number, ? c: tstr, ? d: tdate }
</sourcecode> 
      <t>The JTD schema</t>
<sourcecode type="json"> 
   { "values": { "type": "float32" }}
</sourcecode> 
      <t>accepts the same instances as the CDDL rule</t>
<sourcecode type="cddl"> 
   root = { * tstr => number }
</sourcecode> 
      <t>Finally, the JTD schema</t>
<sourcecode type="json"> 
   {
     "discriminator": "a",
     "mapping": {
       "foo": {
         "properties": {
           "b": { "type": "float32" }
         }
       },
       "bar": {
         "properties": {
           "b": { "type": "string" }
         }
       }
     }
   }
</sourcecode> 
      <t>accepts the same instances as the CDDL rule</t>
<sourcecode type="cddl"> 
   root = { a: "foo", b: number } / { a: "bar", b: tstr }
</sourcecode> 
    </section>
    <section anchor="examples" numbered="true" toc="default">
      <name>Example</name>
      <t>This appendix is not normative.</t>
      <t>As a demonstration of JTD, in <xref target="JTD-reputation-object"
      format="default"/> is a JTD schema closely equivalent to the
      plain-English definition "reputation-object" described in
      <xref target="RFC7071" sectionFormat="of" section="6.2.2"/>:</t> 
      <figure anchor="JTD-reputation-object">
        <name>A JTD Schema Describing "reputation-object" from <xref
	target="RFC7071" sectionFormat="of" section="6.2.2"/></name>
<sourcecode type="json"> 
{
  "properties": {
    "application": { "type": "string" },
    "reputons": {
      "elements": {
        "additionalProperties": true,
        "properties": {
          "rater": { "type": "string" },
          "assertion": { "type": "string" },
          "rated": { "type": "string" },
          "rating": { "type": "float32" },
        },
        "optionalProperties": {
          "confidence": { "type": "float32" },
          "normal-rating": { "type": "float32" },
          "sample-size": { "type": "float64" },
          "generated": { "type": "float64" },
          "expires": { "type": "float64" }
        }
      }
    }
  }
}
</sourcecode> 
      </figure>
      <t>This schema does not enforce the requirement that
      "sample-size", "generated", and "expires" be
      unbounded positive integers. It does not express the limitation that
      "rating", "confidence", and "normal-rating" should
      not have more than three decimal places of precision.</t>
      <t>The example in <xref target="JTD-reputation-object"
      format="default"/> can be compared against the equivalent example in
      <xref target="RFC8610" sectionFormat="of" section="H"/>.</t> 
    </section>
    <section numbered="false" anchor="acknowledgments" toc="default">
      <name>Acknowledgments</name>
      <t><contact fullname="Carsten Bormann"/> provided lots of useful
      guidance and feedback on 
      JTD's design and the structure of this document.</t>
      <t><contact fullname="Evgeny Poberezkin"/> suggested the addition of
      "nullable" and 
      thoroughly vetted this document for mistakes and opportunities for
      simplification.</t> 
      <t><contact fullname="Tim Bray"/> suggested the current "ref"
      model and the addition 
      of "enum". <contact fullname="Anders Rundgren"/> suggested
      extending "type" to 
      have more support for numerical types. <contact fullname="James
      Manger"/> suggested additional 
      clarifying examples of how integer types work. <contact fullname="Adrian
      Farrel"/> suggested 
      many improvements to help make this document clearer.</t>
      <t>Members of the IETF JSON mailing list -- in particular, <contact
      fullname="Pete Cordell"/>, 
      <contact fullname="Phillip Hallam-Baker"/>, <contact fullname="Nico
      Williams"/>, <contact fullname="John Cowan"/>, <contact fullname="Rob
      Sayre"/>, and <contact fullname="Erik Wilde"/> -- provided lots of useful
      feedback.</t> 
      <t>OpenAPI's "discriminator" object <xref target="OPENAPI"
      format="default"/> inspired the "discriminator" form. <xref
      target="I-D.handrews-json-schema" format="default"/> influenced various
      parts of JTD's early design.</t>
    </section>




  </back>

</rfc>
