Created
July 2, 2020 20:07
-
-
Save ucarion/908bcea9feed1f298fc242784c05f643 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="UTF-8"?> | |
<?xml-stylesheet type="text/xsl" href="rfc2629.xslt" ?> | |
<!-- generated by https://github.com/cabo/kramdown-rfc2629 version 1.2.13 --> | |
<!DOCTYPE rfc SYSTEM "rfc2629.dtd" [ | |
<!ENTITY RFC3339 SYSTEM "https://xml2rfc.tools.ietf.org/public/rfc/bibxml/reference.RFC.3339.xml"> | |
<!ENTITY RFC4287 SYSTEM "https://xml2rfc.tools.ietf.org/public/rfc/bibxml/reference.RFC.4287.xml"> | |
<!ENTITY RFC6901 SYSTEM "https://xml2rfc.tools.ietf.org/public/rfc/bibxml/reference.RFC.6901.xml"> | |
<!ENTITY RFC8259 SYSTEM "https://xml2rfc.tools.ietf.org/public/rfc/bibxml/reference.RFC.8259.xml"> | |
<!ENTITY RFC8610 SYSTEM "https://xml2rfc.tools.ietf.org/public/rfc/bibxml/reference.RFC.8610.xml"> | |
<!ENTITY RFC2119 SYSTEM "https://xml2rfc.tools.ietf.org/public/rfc/bibxml/reference.RFC.2119.xml"> | |
<!ENTITY RFC8174 SYSTEM "https://xml2rfc.tools.ietf.org/public/rfc/bibxml/reference.RFC.8174.xml"> | |
<!ENTITY RFC7071 SYSTEM "https://xml2rfc.tools.ietf.org/public/rfc/bibxml/reference.RFC.7071.xml"> | |
<!ENTITY RFC7493 SYSTEM "https://xml2rfc.tools.ietf.org/public/rfc/bibxml/reference.RFC.7493.xml"> | |
<!ENTITY I-D.handrews-json-schema SYSTEM "https://xml2rfc.tools.ietf.org/public/rfc/bibxml3/reference.I-D.handrews-json-schema.xml"> | |
]> | |
<?rfc toc="yes"?> | |
<?rfc sortrefs="yes"?> | |
<?rfc symrefs="yes"?> | |
<rfc ipr="trust200902" docName="draft-ucarion-json-type-definition-04" category="exp"> | |
<front> | |
<title>JSON Type Definition</title> | |
<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> | |
<code>94111</code> | |
<country>United States of America</country> | |
</postal> | |
<email>[email protected]</email> | |
</address> | |
</author> | |
<date year="2020" month="June" day="28"/> | |
<area>Applications</area> | |
<workgroup>Independent Submission</workgroup> | |
<keyword>Internet-Draft</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" title="Introduction"> | |
<t>This document describes a schema language for JSON <xref target="RFC8259"/> 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> | |
<t><list style="symbols"> | |
<t>Provide an unambiguous description of the overall structure of a JSON | |
document.</t> | |
<t>Be able to describe common JSON datatypes and structures. That is, the | |
datatypes and structures necessary to support most JSON documents, and which | |
are widely understood in an interoperable way by JSON implementations.</t> | |
<t>Provide a single format that is readable and editable by both humans and | |
machines, and which can be embedded within other JSON documents. This makes | |
JTD a convenient format for tooling to accept as input or produce as output.</t> | |
<t>Enable code generation from JTD schemas. JTD schemas are meant to be easy to | |
convert into data structures idiomatic to mainstream programming languages.</t> | |
<t>Provide a standardized format for error indicators when data does not conform | |
with a schema.</t> | |
</list></t> | |
<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"/> 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> | |
<t><list style="symbols"> | |
<t>Signed and unsigned 8, 16, and 32-bit integers. A tool which converts JTD | |
schemas into code can use <spanx style="verb">int8_t</spanx>, <spanx style="verb">uint8_t</spanx>, <spanx style="verb">int16_t</spanx>, etc., or their | |
equivalents in the target language, to represent these JTD types.</t> | |
<t>A distinction between <spanx style="verb">float32</spanx> and <spanx style="verb">float64</spanx>. Code generators can use <spanx style="verb">float</spanx> | |
and <spanx style="verb">double</spanx>, or their equivalents, for these JTD types.</t> | |
<t>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++ <spanx style="verb">struct</spanx>.</t> | |
<t>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++ | |
<spanx style="verb">std::map</spanx>.</t> | |
<t>A “discriminator” form of JSON objects, corresponding to a discriminated (or | |
“tagged”) union. The “discriminator” form of JSON objects is akin to a C++ | |
<spanx style="verb">std::variant</spanx>.</t> | |
</list></t> | |
<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 a non-interoperable | |
(i.e., ignoring the recommendations in Section 2.2 of <xref target="RFC7493"/>) or mutually | |
inconsistent ways. <xref target="other-considerations-int64"/> 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:</t> | |
<t><xref target="syntax"/> defines the syntax of JTD. <xref target="semantics"/> 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"/> discusses why certain features are omitted from JTD. | |
<xref target="comparison-with-cddl"/> presents various JTD schemas and their CDDL | |
equivalents.</t> | |
<section anchor="terminology" title="Terminology"> | |
<t>The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL | |
NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “NOT RECOMMENDED”, | |
“MAY”, and “OPTIONAL” in this document are to be interpreted as | |
described in BCP 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"/>.</t> | |
<t>The terms “object”, “member”, “array”, “number”, “name”, and “string” in this | |
document are to be interpreted as described in <xref target="RFC8259"/>.</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" title="Scope of Experiment"> | |
<t>JTD is an experiment. Participation in this experiment consists of using JTD to | |
validate or document interchanged JSON messages, or in building tooling atop of | |
JTD. Feedback on the results of this experiment may be e-mailed 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 <https://github.com/jsontypedef>.</t> | |
</section> | |
</section> | |
<section anchor="syntax" title="Syntax"> | |
<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 which 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"/>. Root JTD | |
schemas may optionally contain definitions (a mapping from names to schemas).</t> | |
<t>A correct root JTD schema MUST match the <spanx style="verb">root-schema</spanx> CDDL rule described in | |
this section. A correct non-root JTD schema MUST match the <spanx style="verb">schema</spanx> CDDL rule | |
described in this section.</t> | |
<figure title="CDDL definition of a schema" anchor="cddl-schema"><artwork type="cddl"><![CDATA[ | |
; 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, | |
) | |
]]></artwork></figure> | |
<t>The remainder of this section will describe constraints on JTD schemas which | |
cannot be expressed in CDDL, and will provide examples of valid and invalid JTD | |
schemas.</t> | |
<section anchor="root-vs-non-root-schemas" title="Root vs. non-root schemas"> | |
<t>The <spanx style="verb">root-schema</spanx> rule in <xref target="cddl-schema"/> permits for a member named | |
<spanx style="verb">definitions</spanx>, but the <spanx style="verb">schema</spanx> rule does not permit for such a member. This | |
means that only root (i.e., “top-level”) JTD schemas can have a <spanx style="verb">definitions</spanx> | |
object, and sub-schemas may not.</t> | |
<t>Thus</t> | |
<figure><artwork type="json"><![CDATA[ | |
{ "definitions": {} } | |
]]></artwork></figure> | |
<t>is a correct JTD schema, but</t> | |
<figure><artwork type="json"><![CDATA[ | |
{ | |
"definitions": { | |
"foo": { | |
"definitions": {} | |
} | |
} | |
} | |
]]></artwork></figure> | |
<t>is not, because sub-schemas (such as the object at <spanx style="verb">/definitions/foo</spanx>) must not | |
have a member named <spanx style="verb">definitions</spanx>.</t> | |
</section> | |
<section anchor="forms" title="Forms"> | |
<t>JTD schemas (i.e. JSON objects satisfying the <spanx style="verb">schema</spanx> CDDL rule in | |
<xref target="cddl-schema"/>) 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" title="Empty"> | |
<t>The <spanx style="verb">empty</spanx> form is defined by the <spanx style="verb">empty</spanx> CDDL rule in <xref target="cddl-schema"/>. The | |
semantics of the <spanx style="verb">empty</spanx> form are described in <xref target="semantics-form-empty"/>.</t> | |
<t>Despite the name “empty”, schemas of the <spanx style="verb">empty</spanx> form are not necessarily empty | |
JSON objects. Like schemas of any of the eight forms, schemas of the <spanx style="verb">empty</spanx> | |
form may contain members named <spanx style="verb">nullable</spanx> (whose value must be <spanx style="verb">true</spanx> or | |
<spanx style="verb">false</spanx>) or <spanx style="verb">metadata</spanx> (whose value must be an object) or both.</t> | |
<t>Thus</t> | |
<figure><artwork type="json"><![CDATA[ | |
{} | |
]]></artwork></figure> | |
<t>and</t> | |
<figure><artwork type="json"><![CDATA[ | |
{ "nullable": true } | |
]]></artwork></figure> | |
<t>and</t> | |
<figure><artwork type="json"><![CDATA[ | |
{ "nullable": true, "metadata": { "foo": "bar" }} | |
]]></artwork></figure> | |
<t>are correct JTD schemas of the empty form, but</t> | |
<figure><artwork type="json"><![CDATA[ | |
{ "nullable": "foo" } | |
]]></artwork></figure> | |
<t>is not, because the value of the member named <spanx style="verb">nullable</spanx> must be <spanx style="verb">true</spanx> or | |
<spanx style="verb">false</spanx>.</t> | |
</section> | |
<section anchor="syntax-form-ref" title="Ref"> | |
<t>The <spanx style="verb">ref</spanx> form is defined by the <spanx style="verb">ref</spanx> CDDL rule in <xref target="cddl-schema"/>. The | |
semantics of the <spanx style="verb">ref</spanx> form are described in <xref target="semantics-form-ref"/>.</t> | |
<t>For a schema of the <spanx style="verb">ref</spanx> form to be correct, the value of the member named | |
<spanx style="verb">ref</spanx> must refer to one of the definitions found at the root level of the schema | |
it appears in. More formally, for a schema <spanx style="emph">S</spanx> of the <spanx style="verb">ref</spanx> form:</t> | |
<t><list style="symbols"> | |
<t>Let <spanx style="emph">B</spanx> be the root schema containing the schema, or the schema itself if it | |
is a root schema.</t> | |
<t>Let <spanx style="emph">R</spanx> be the value of the member of <spanx style="emph">S</spanx> with the name <spanx style="verb">ref</spanx>.</t> | |
</list></t> | |
<t>If the schema is correct, then <spanx style="emph">B</spanx> MUST have a member <spanx style="emph">D</spanx> with the name | |
<spanx style="verb">definitions</spanx>, and <spanx style="emph">D</spanx> MUST contain a member whose name equals <spanx style="emph">R</spanx>.</t> | |
<t>Thus</t> | |
<figure><artwork type="json"><![CDATA[ | |
{ | |
"definitions": { | |
"coordinates": { | |
"properties": { | |
"lat": { "type": "float32" }, | |
"lng": { "type": "float32" } | |
} | |
} | |
}, | |
"properties": { | |
"user_location": { "ref": "coordinates" }, | |
"server_location": { "ref": "coordinates" } | |
} | |
} | |
]]></artwork></figure> | |
<t>is a correct JTD schema, and demonstrates the point of the <spanx style="verb">ref</spanx> form: to avoid | |
re-defining the same thing twice. However,</t> | |
<figure><artwork type="json"><![CDATA[ | |
{ "ref": "foo" } | |
]]></artwork></figure> | |
<t>is not a correct JTD schema, as there is no top-level <spanx style="verb">definitions</spanx>, and so the | |
<spanx style="verb">ref</spanx> form cannot be correct. Similarly,</t> | |
<figure><artwork type="json"><![CDATA[ | |
{ "definitions": { "foo": {}}, "ref": "bar" } | |
]]></artwork></figure> | |
<t>is not a correct JTD schema, as there is no member named <spanx style="verb">bar</spanx> in the top-level | |
<spanx style="verb">definitions</spanx>.</t> | |
</section> | |
<section anchor="syntax-form-type" title="Type"> | |
<t>The <spanx style="verb">type</spanx> form is defined by the <spanx style="verb">type</spanx> CDDL rule in <xref target="cddl-schema"/>. The | |
semantics of the <spanx style="verb">type</spanx> form are described in <xref target="semantics-form-type"/>.</t> | |
<t>As an example of a correct JTD schema of the <spanx style="verb">type</spanx> form,</t> | |
<figure><artwork type="json"><![CDATA[ | |
{ "type": "uint8" } | |
]]></artwork></figure> | |
<t>is a correct JTD schema, whereas</t> | |
<figure><artwork type="json"><![CDATA[ | |
{ "type": true } | |
]]></artwork></figure> | |
<t>and</t> | |
<figure><artwork type="json"><![CDATA[ | |
{ "type": "foo" } | |
]]></artwork></figure> | |
<t>are not correct schemas, as neither <spanx style="verb">true</spanx> nor the JSON string <spanx style="verb">foo</spanx> are in the | |
list of permitted values of the <spanx style="verb">type</spanx> member described in the <spanx style="verb">type</spanx> CDDL rule | |
in <xref target="cddl-schema"/>.</t> | |
</section> | |
<section anchor="syntax-form-enum" title="Enum"> | |
<t>The <spanx style="verb">enum</spanx> form is defined by the <spanx style="verb">enum</spanx> CDDL rule in <xref target="cddl-schema"/>. The | |
semantics of the <spanx style="verb">enum</spanx> form are described in <xref target="semantics-form-enum"/>.</t> | |
<t>For a schema of the <spanx style="verb">enum</spanx> form to be correct, the value of the member named | |
<spanx style="verb">enum</spanx> must be a nonempty array of strings, and that array must not contain | |
duplicate values. More formally, for a schema <spanx style="emph">S</spanx> of the <spanx style="verb">enum</spanx> form:</t> | |
<t><list style="symbols"> | |
<t>Let <spanx style="emph">E</spanx> be the value of the member of <spanx style="emph">S</spanx> with name <spanx style="verb">enum</spanx>.</t> | |
</list></t> | |
<t>If the schema is correct, then there MUST NOT exist any pair of elements of <spanx style="emph">E</spanx> | |
which encode equal string values, where string equality is defined as in Section | |
8.3 of <xref target="RFC8259"/>.</t> | |
<t>Thus</t> | |
<figure><artwork type="json"><![CDATA[ | |
{ "enum": [] } | |
]]></artwork></figure> | |
<t>is not a correct JTD schema, as the value of the member named <spanx style="verb">enum</spanx> must be | |
nonempty, and</t> | |
<figure><artwork type="json"><![CDATA[ | |
{ "enum": ["a\\b", "a\u005Cb"] } | |
]]></artwork></figure> | |
<t>is not a correct JTD schema, as</t> | |
<figure><artwork type="json"><![CDATA[ | |
"a\\b" | |
]]></artwork></figure> | |
<t>and</t> | |
<figure><artwork type="json"><![CDATA[ | |
"a\u005Cb" | |
]]></artwork></figure> | |
<t>encode strings that are equal by the definition of string equality given in | |
Section 8.3 of <xref target="RFC8259"/>. By contrast,</t> | |
<figure><artwork type="json"><![CDATA[ | |
{ "enum": ["PENDING", "IN_PROGRESS", "DONE" ]} | |
]]></artwork></figure> | |
<t>is an example of a correct JTD schema of the <spanx style="verb">enum</spanx> form.</t> | |
</section> | |
<section anchor="syntax-form-elements" title="Elements"> | |
<t>The <spanx style="verb">elements</spanx> form is defined by the <spanx style="verb">elements</spanx> CDDL rule in <xref target="cddl-schema"/>. | |
The semantics of the <spanx style="verb">elements</spanx> form are described in | |
<xref target="semantics-form-elements"/>.</t> | |
<t>As an example of a correct JTD schema of the <spanx style="verb">elements</spanx> form,</t> | |
<figure><artwork type="json"><![CDATA[ | |
{ "elements": { "type": "uint8" }} | |
]]></artwork></figure> | |
<t>is a correct JTD schema, whereas</t> | |
<figure><artwork type="json"><![CDATA[ | |
{ "elements": true } | |
]]></artwork></figure> | |
<t>and</t> | |
<figure><artwork type="json"><![CDATA[ | |
{ "elements": { "type": "foo" } } | |
]]></artwork></figure> | |
<t>are not correct schemas, as neither</t> | |
<figure><artwork type="json"><![CDATA[ | |
true | |
]]></artwork></figure> | |
<t>nor</t> | |
<figure><artwork type="json"><![CDATA[ | |
{ "type": "foo" } | |
]]></artwork></figure> | |
<t>are correct JTD schemas, and the value of the member named <spanx style="verb">elements</spanx> must be a | |
correct JTD schema.</t> | |
</section> | |
<section anchor="syntax-form-properties" title="Properties"> | |
<t>The <spanx style="verb">properties</spanx> form is defined by the <spanx style="verb">properties</spanx> CDDL rule in | |
<xref target="cddl-schema"/>. The semantics of the <spanx style="verb">properties</spanx> form are described in | |
<xref target="semantics-form-props"/>.</t> | |
<t>For a schema of the <spanx style="verb">properties</spanx> form to be correct, properties must either be | |
required (i.e., in <spanx style="verb">properties</spanx>) or optional (i.e., in <spanx style="verb">optionalProperties</spanx>), | |
but not both. More formally:</t> | |
<t>If a schema has both a member named <spanx style="verb">properties</spanx> (with value <spanx style="emph">P</spanx>) and another | |
member named <spanx style="verb">optionalProperties</spanx> (with value <spanx style="emph">O</spanx>), then <spanx style="emph">O</spanx> and <spanx style="emph">P</spanx> MUST NOT | |
have any member names in common; that is, no member of <spanx style="emph">P</spanx> may have a name equal | |
to the name of any member of <spanx style="emph">O</spanx>, under the definition of string equality given | |
in Section 8.3 of <xref target="RFC8259"/>.</t> | |
<t>Thus</t> | |
<figure><artwork type="json"><![CDATA[ | |
{ | |
"properties": { "confusing": {} }, | |
"optionalProperties": { "confusing": {} } | |
} | |
]]></artwork></figure> | |
<t>is not a correct JTD schema, as <spanx style="verb">confusing</spanx> appears in both <spanx style="verb">properties</spanx> and | |
<spanx style="verb">optionalProperties</spanx>. By contrast,</t> | |
<figure><artwork type="json"><![CDATA[ | |
{ | |
"properties": { | |
"users": { | |
"elements": { | |
"properties": { | |
"id": { "type": "string" }, | |
"name": { "type": "string" }, | |
"create_time": { "type": "timestamp" } | |
}, | |
"optionalProperties": { | |
"delete_time": { "type": "timestamp" } | |
} | |
} | |
}, | |
"next_page_token": { "type": "string" } | |
} | |
} | |
]]></artwork></figure> | |
<t>is a correct JTD schema of the <spanx style="verb">properties</spanx> 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" title="Values"> | |
<t>The <spanx style="verb">values</spanx> form is defined by the <spanx style="verb">values</spanx> CDDL rule in <xref target="cddl-schema"/>. The | |
semantics of the <spanx style="verb">values</spanx> form are described in <xref target="semantics-form-values"/>.</t> | |
<t>As an example of a correct JTD schema of the <spanx style="verb">values</spanx> form,</t> | |
<figure><artwork type="json"><![CDATA[ | |
{ "values": { "type": "uint8" }} | |
]]></artwork></figure> | |
<t>is a correct JTD schema, whereas</t> | |
<figure><artwork type="json"><![CDATA[ | |
{ "values": true } | |
]]></artwork></figure> | |
<t>and</t> | |
<figure><artwork type="json"><![CDATA[ | |
{ "values": { "type": "foo" } } | |
]]></artwork></figure> | |
<t>are not correct schemas, as neither</t> | |
<figure><artwork type="json"><![CDATA[ | |
true | |
]]></artwork></figure> | |
<t>nor</t> | |
<figure><artwork type="json"><![CDATA[ | |
{ "type": "foo" } | |
]]></artwork></figure> | |
<t>are correct JTD schemas, and the value of the member named <spanx style="verb">values</spanx> must be a | |
correct JTD schema.</t> | |
</section> | |
<section anchor="syntax-form-discriminator" title="Discriminator"> | |
<t>The <spanx style="verb">discriminator</spanx> form is defined by the <spanx style="verb">discriminator</spanx> CDDL rule in | |
<xref target="cddl-schema"/>. The semantics of the <spanx style="verb">discriminator</spanx> form are described in | |
<xref target="semantics-form-discriminator"/>. Understanding the semantics of the | |
<spanx style="verb">discriminator</spanx> form will likely aid the reader in understanding why this | |
section provides constraints on the <spanx style="verb">discriminator</spanx> form beyond those in | |
<xref target="cddl-schema"/>.</t> | |
<t>To prevent ambiguous or unsatisfiable constraints on the <spanx style="verb">discriminator</spanx> | |
property of a tagged union, an additional constraint on schemas of the | |
<spanx style="verb">discriminator</spanx> form exists. For schemas of the discriminator form:</t> | |
<t><list style="symbols"> | |
<t>Let <spanx style="emph">D</spanx> be the member of the schema with the name <spanx style="verb">discriminator</spanx>.</t> | |
<t>Let <spanx style="emph">M</spanx> be the member of the schema with the name <spanx style="verb">mapping</spanx>.</t> | |
</list></t> | |
<t>If the schema is correct, then all member values <spanx style="emph">S</spanx> of <spanx style="emph">M</spanx> will be schemas of | |
the “properties” form. For each <spanx style="emph">S</spanx>:</t> | |
<t><list style="symbols"> | |
<t>If <spanx style="emph">S</spanx> has a member <spanx style="emph">N</spanx> whose name equals <spanx style="verb">nullable</spanx>, <spanx style="emph">N</spanx>’s value MUST NOT be | |
the JSON primitive value <spanx style="verb">true</spanx>.</t> | |
<t>For each member <spanx style="emph">P</spanx> of <spanx style="emph">S</spanx> whose name equals <spanx style="verb">properties</spanx> or | |
<spanx style="verb">optionalProperties</spanx>, <spanx style="emph">P</spanx>’s value, which must be an object, MUST NOT contain | |
any members whose name equals <spanx style="emph">D</spanx>’s value.</t> | |
</list></t> | |
<t>Thus</t> | |
<figure><artwork type="json"><![CDATA[ | |
{ | |
"discriminator": "event_type", | |
"mapping": { | |
"can_the_object_be_null_or_not?": { | |
"nullable": true, | |
"properties": { "foo": { "type": "string" } }} | |
} | |
} | |
} | |
]]></artwork></figure> | |
<t>is an incorrect schema, as a member of <spanx style="verb">mapping</spanx> has a member named <spanx style="verb">nullable</spanx> | |
whose value is <spanx style="verb">true</spanx>. This would suggest that the instance may be null. Yet the | |
top-level schema lacks such a <spanx style="verb">nullable</spanx> set to <spanx style="verb">true</spanx>, 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 <spanx style="verb">discriminator</spanx> form may be null. The schemas in a discriminator | |
<spanx style="verb">mapping</spanx> cannot have <spanx style="verb">nullable</spanx> set to <spanx style="verb">true</spanx>; only the discriminator itself | |
can use <spanx style="verb">nullable</spanx> in this way.</t> | |
<t>It also follows that</t> | |
<figure><artwork type="json"><![CDATA[ | |
{ | |
"discriminator": "event_type", | |
"mapping": { | |
"is_event_type_a_string_or_a_float32?": { | |
"properties": { "event_type": { "type": "float32" }} | |
} | |
} | |
} | |
]]></artwork></figure> | |
<t>and</t> | |
<figure><artwork type="json"><![CDATA[ | |
{ | |
"discriminator": "event_type", | |
"mapping": { | |
"is_event_type_a_string_or_an_optional_float32?": { | |
"optionalProperties": { "event_type": { "type": "float32" }} | |
} | |
} | |
} | |
]]></artwork></figure> | |
<t>are incorrect schemas, as <spanx style="verb">event_type</spanx> is both the value of <spanx style="verb">discriminator</spanx> and | |
a member name in one of the <spanx style="verb">mapping</spanx> member <spanx style="verb">properties</spanx> or | |
<spanx style="verb">optionalProperties</spanx>. This is ambiguous, because ordinarily the <spanx style="verb">discriminator</spanx> | |
keyword would indicate that <spanx style="verb">event_type</spanx> is expected to be a string, but another | |
part of the schema specifies that <spanx style="verb">event_type</spanx> 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 re-defined in other parts of the schema.</t> | |
<t>By contrast,</t> | |
<figure><artwork type="json"><![CDATA[ | |
{ | |
"tag": "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" } | |
} | |
} | |
} | |
} | |
]]></artwork></figure> | |
<t>is a correct schema, describing a pattern of data common in JSON-based messaging | |
systems. <xref target="semantics-form-discriminator"/> provides examples of what this schema | |
accepts and rejects.</t> | |
</section> | |
</section> | |
<section anchor="extending-JTD-syntax" title="Extending JTD’s Syntax"> | |
<t>This document does not describe any extension mechanisms for JTD schema | |
validation, which is described in <xref target="semantics"/>. However, schemas are defined to | |
optionally contain a <spanx style="verb">metadata</spanx> keyword, whose value is an arbitrary JSON | |
object. Call the members of this object “metadata members”.</t> | |
<t>Users MAY 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 SHOULD NOT expect metadata members to be understood by other parties. As a | |
result, if consistent validation with other parties is a requirement, users MUST | |
NOT use metadata members to affect how schema validation, as described in | |
<xref target="semantics"/>, works.</t> | |
<t>Users MAY expect metadata members to be understood by other parties, and MAY 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" title="Semantics"> | |
<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" title="Allowing Additional Properties"> | |
<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"/>:</t> | |
<figure title="An illustrative JTD schema" anchor="JTD-properties-a"><artwork type="json"><![CDATA[ | |
{ "properties": { "a": { "type": "string" }}} | |
]]></artwork></figure> | |
<t>Some users may expect that</t> | |
<figure><artwork type="json"><![CDATA[ | |
{"a": "foo", "b": "bar"} | |
]]></artwork></figure> | |
<t>satisfies the schema in <xref target="JTD-properties-a"/>. Others may disagree, as <spanx style="verb">b</spanx> is | |
not one of the properties described in the schema. In this document, allowing | |
such “unspecified” members, like <spanx style="verb">b</spanx> 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 can | |
be overridden by having the schema include a member named | |
<spanx style="verb">additionalProperties</spanx>, where that member has a value of <spanx style="verb">true</spanx>.</t> | |
<t>More formally: evaluation of a schema <spanx style="emph">S</spanx> is in “allow additional properties” | |
mode if there exists a member of <spanx style="emph">S</spanx> whose name equals <spanx style="verb">additionalProperties</spanx>, | |
and whose value is a boolean <spanx style="verb">true</spanx>. Otherwise, evaluation of <spanx style="emph">S</spanx> is not in | |
“allow additional properties” mode.</t> | |
<t>See <xref target="semantics-form-props"/> for how allowing unknown properties affects schema | |
evaluation, but briefly, the schema</t> | |
<figure><artwork type="json"><![CDATA[ | |
{ "properties": { "a": { "type": "string" }}} | |
]]></artwork></figure> | |
<t>rejects</t> | |
<figure><artwork type="json"><![CDATA[ | |
{ "a": "foo", "b": "bar" } | |
]]></artwork></figure> | |
<t>However, the schema</t> | |
<figure><artwork type="json"><![CDATA[ | |
{ | |
"additionalProperties": true, | |
"properties": { "a": { "type": "string" }} | |
} | |
]]></artwork></figure> | |
<t>accepts</t> | |
<figure><artwork type="json"><![CDATA[ | |
{ "a": "foo", "b": "bar" } | |
]]></artwork></figure> | |
<t>Note that <spanx style="verb">additionalProperties</spanx> does not get “inherited” by sub-schemas. For | |
example, the JTD schema</t> | |
<figure><artwork type="json"><![CDATA[ | |
{ | |
"additionalProperties": true, | |
"properties": { | |
"a": { | |
"properties": { | |
"b": { "type": "string" } | |
} | |
} | |
} | |
} | |
]]></artwork></figure> | |
<t>accepts</t> | |
<figure><artwork type="json"><![CDATA[ | |
{ "a": { "b": "c" }, "foo": "bar" } | |
]]></artwork></figure> | |
<t>but rejects</t> | |
<figure><artwork type="json"><![CDATA[ | |
{ "a": { "b": "c", "foo": "bar" }} | |
]]></artwork></figure> | |
<t>because the <spanx style="verb">additionalProperties</spanx> at the root level does not affect the | |
behavior of sub-schemas.</t> | |
<t>Note from <xref target="cddl-schema"/> that only schemas of the <spanx style="verb">properties</spanx> form may have a | |
member named <spanx style="verb">additionalProperties</spanx>.</t> | |
</section> | |
<section anchor="errors" title="Errors"> | |
<t>To facilitate consistent validation error handling, this document specifies a | |
standard error indicator format. Implementations SHOULD 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> | |
<t><list style="symbols"> | |
<t>A member with the name <spanx style="verb">instancePath</spanx>, whose value is a JSON string encoding a | |
JSON Pointer. This JSON Pointer will point to the part of the instance that | |
was rejected.</t> | |
<t>A member with the name <spanx style="verb">schemaPath</spanx>, 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.</t> | |
</list></t> | |
<t>The values for <spanx style="verb">instancePath</spanx> and <spanx style="verb">schemaPath</spanx> depend on the form of the schema, | |
and are described in detail in <xref target="semantics-forms"/>.</t> | |
</section> | |
<section anchor="semantics-forms" title="Forms"> | |
<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"/>.</t> | |
<section anchor="semantics-form-empty" title="Empty"> | |
<t>The <spanx style="verb">empty</spanx> form is meant to describe instances whose values are unknown, | |
unpredictable, or otherwise unconstrained by the schema. The syntax of the | |
<spanx style="verb">empty</spanx> form is described in <xref target="syntax-form-empty"/>.</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" title="Ref"> | |
<t>The <spanx style="verb">ref</spanx> form is for when a schema is defined in terms of something in the | |
<spanx style="verb">definitions</spanx> of the root schema. The ref form enables schemas to be less | |
repetitive, and also enables describing recursive structures. The syntax of the | |
<spanx style="verb">ref</spanx> form is described in <xref target="syntax-form-ref"/>.</t> | |
<t>If a schema is of the ref form, then:</t> | |
<t><list style="symbols"> | |
<t>If the schema has a member named <spanx style="verb">nullable</spanx> whose value is the boolean <spanx style="verb">true</spanx>, | |
and the instance is the JSON primitive value <spanx style="verb">null</spanx>, then the schema accepts | |
the instance. Otherwise:</t> | |
<t>Let <spanx style="emph">B</spanx> be the root schema containing the schema, or the schema itself if it | |
is a root schema.</t> | |
<t>Let <spanx style="emph">D</spanx> be the member of <spanx style="emph">B</spanx> with the name <spanx style="verb">definitions</spanx>. By <xref target="syntax"/>, <spanx style="emph">D</spanx> | |
exists.</t> | |
<t>Let <spanx style="emph">R</spanx> be the value of the schema member with the name <spanx style="verb">ref</spanx>.</t> | |
<t>Let <spanx style="emph">S</spanx> be the value of the member of <spanx style="emph">D</spanx> whose name equals <spanx style="emph">R</spanx>. By | |
<xref target="syntax-form-ref"/>, <spanx style="emph">S</spanx> exists, and is a schema.</t> | |
</list></t> | |
<t>The schema accepts the instance if and only if <spanx style="emph">S</spanx> accepts the instance. | |
Otherwise, the error indicators to return in this case are the union of the | |
error indicators from evaluating <spanx style="emph">S</spanx> against the instance.</t> | |
<t>For example, the schema:</t> | |
<figure><artwork type="json"><![CDATA[ | |
{ | |
"definitions": { "a": { "type": "float32" }}, | |
"ref": "a" | |
} | |
]]></artwork></figure> | |
<t>accepts</t> | |
<figure><artwork type="json"><![CDATA[ | |
123 | |
]]></artwork></figure> | |
<t>but rejects</t> | |
<figure><artwork type="json"><![CDATA[ | |
null | |
]]></artwork></figure> | |
<t>with the error indicator</t> | |
<figure><artwork type="json"><![CDATA[ | |
[{ "instancePath": "", "schemaPath": "/definitions/a/type" }] | |
]]></artwork></figure> | |
<t>The schema</t> | |
<figure><artwork type="json"><![CDATA[ | |
{ | |
"definitions": { "a": { "type": "float32" }}, | |
"ref": "a", | |
"nullable": true | |
} | |
]]></artwork></figure> | |
<t>accepts</t> | |
<figure><artwork type="json"><![CDATA[ | |
null | |
]]></artwork></figure> | |
<t>because the schema has a <spanx style="verb">nullable</spanx> member, whose value is <spanx style="verb">true</spanx>.</t> | |
<t>Note that <spanx style="verb">nullable</spanx> being <spanx style="verb">false</spanx> has no effect in any of the forms described | |
in this document. For example, the schema</t> | |
<figure><artwork type="json"><![CDATA[ | |
{ | |
"definitions": { "a": { "nullable": false, "type": "float32" }}, | |
"ref": "a", | |
"nullable": true | |
} | |
]]></artwork></figure> | |
<t>accepts</t> | |
<figure><artwork type="json"><![CDATA[ | |
null | |
]]></artwork></figure> | |
<t>In other words, it is not the case that putting a <spanx style="verb">false</spanx> value for <spanx style="verb">nullable</spanx> | |
will ever “override” a <spanx style="verb">nullable</spanx> member in schemas of the <spanx style="verb">ref</spanx> form; it is | |
correct, though ineffectual, to have a value of <spanx style="verb">false</spanx> for the <spanx style="verb">nullable</spanx> | |
member in a schema.</t> | |
</section> | |
<section anchor="semantics-form-type" title="Type"> | |
<t>The <spanx style="verb">type</spanx> form is meant to describe instances whose value is a boolean, number, | |
string, or timestamp (<xref target="RFC3339"/>). The syntax of the <spanx style="verb">type</spanx> form is described | |
in <xref target="syntax-form-type"/>.</t> | |
<t>If a schema is of the type form, then:</t> | |
<t><list style="symbols"> | |
<t>If the schema has a member named <spanx style="verb">nullable</spanx> whose value is the boolean <spanx style="verb">true</spanx>, | |
and the instance is the JSON primitive value <spanx style="verb">null</spanx>, then the schema accepts | |
the instance. Otherwise:</t> | |
<t>Let <spanx style="emph">T</spanx> be the value of the member with the name <spanx style="verb">type</spanx>. The following table | |
describes whether the instance is accepted, as a function of <spanx style="emph">T</spanx>’s value:</t> | |
</list></t> | |
<texttable title="Accepted Values for Type" anchor="type-values"> | |
<ttcol align='left'>If _T_ equals …</ttcol> | |
<ttcol align='left'>then the instance is accepted if it is …</ttcol> | |
<c>boolean</c> | |
<c>equal to <spanx style="verb">true</spanx> or <spanx style="verb">false</spanx></c> | |
<c>float32</c> | |
<c>a JSON number</c> | |
<c>float64</c> | |
<c>a JSON number</c> | |
<c>int8</c> | |
<c>See <xref target="int-ranges"/></c> | |
<c>uint8</c> | |
<c>See <xref target="int-ranges"/></c> | |
<c>int16</c> | |
<c>See <xref target="int-ranges"/></c> | |
<c>uint16</c> | |
<c>See <xref target="int-ranges"/></c> | |
<c>int32</c> | |
<c>See <xref target="int-ranges"/></c> | |
<c>uint32</c> | |
<c>See <xref target="int-ranges"/></c> | |
<c>string</c> | |
<c>a JSON string</c> | |
<c>timestamp</c> | |
<c>a JSON string that follows the standard format described in <xref target="RFC3339"/>, as refined by Section 3.3 of <xref target="RFC4287"/></c> | |
</texttable> | |
<t><spanx style="verb">float32</spanx> and <spanx style="verb">float64</spanx> are distinguished from each other in their intent. | |
<spanx style="verb">float32</spanx> indicates data intended to be processed as an IEEE 754 | |
single-precision float, whereas <spanx style="verb">float64</spanx> indicates data intended to be | |
processed as an IEEE 754 double-precision float. Tools which generate code from | |
JTD schemas will likely produce different code for <spanx style="verb">float32</spanx> than for | |
<spanx style="verb">float64</spanx>.</t> | |
<t>If <spanx style="emph">T</spanx> starts with <spanx style="verb">int</spanx> or <spanx style="verb">uint</spanx>, 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 <spanx style="emph">T</spanx>, this encoded number must additionally fall within a particular | |
range:</t> | |
<texttable title="Ranges for Integer Types" anchor="int-ranges"> | |
<ttcol align='left'>_T_</ttcol> | |
<ttcol align='left'>Minimum Value (Inclusive)</ttcol> | |
<ttcol align='left'>Maximum Value (Inclusive)</ttcol> | |
<c>int8</c> | |
<c>-128</c> | |
<c>127</c> | |
<c>uint8</c> | |
<c>0</c> | |
<c>255</c> | |
<c>int16</c> | |
<c>-32,768</c> | |
<c>32,767</c> | |
<c>uint16</c> | |
<c>0</c> | |
<c>65,535</c> | |
<c>int32</c> | |
<c>-2,147,483,648</c> | |
<c>2,147,483,647</c> | |
<c>uint32</c> | |
<c>0</c> | |
<c>4,294,967,295</c> | |
</texttable> | |
<t>Note that</t> | |
<figure><artwork type="json"><![CDATA[ | |
10 | |
]]></artwork></figure> | |
<t>and</t> | |
<figure><artwork type="json"><![CDATA[ | |
10.0 | |
]]></artwork></figure> | |
<t>and</t> | |
<figure><artwork type="json"><![CDATA[ | |
1.0e1 | |
]]></artwork></figure> | |
<t>encode values with zero fractional part, whereas</t> | |
<figure><artwork type="json"><![CDATA[ | |
10.5 | |
]]></artwork></figure> | |
<t>encodes a number with a non-zero fractional part. Thus the schema</t> | |
<figure><artwork type="json"><![CDATA[ | |
{"type": "int8"} | |
]]></artwork></figure> | |
<t>accepts</t> | |
<figure><artwork type="json"><![CDATA[ | |
10 | |
]]></artwork></figure> | |
<t>and</t> | |
<figure><artwork type="json"><![CDATA[ | |
10.0 | |
]]></artwork></figure> | |
<t>and</t> | |
<figure><artwork type="json"><![CDATA[ | |
1.0e1 | |
]]></artwork></figure> | |
<t>but rejects</t> | |
<figure><artwork type="json"><![CDATA[ | |
10.5 | |
]]></artwork></figure> | |
<t>as well as</t> | |
<figure><artwork type="json"><![CDATA[ | |
false | |
]]></artwork></figure> | |
<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 <spanx style="verb">instancePath</spanx> pointing to the instance, and a <spanx style="verb">schemaPath</spanx> pointing to | |
the schema member with the name <spanx style="verb">type</spanx>.</t> | |
<t>For example, the schema:</t> | |
<figure><artwork type="json"><![CDATA[ | |
{"type": "boolean"} | |
]]></artwork></figure> | |
<t>accepts</t> | |
<figure><artwork type="json"><![CDATA[ | |
false | |
]]></artwork></figure> | |
<t>but rejects</t> | |
<figure><artwork type="json"><![CDATA[ | |
127 | |
]]></artwork></figure> | |
<t>The schema:</t> | |
<figure><artwork type="json"><![CDATA[ | |
{"type": "float32"} | |
]]></artwork></figure> | |
<t>accepts</t> | |
<figure><artwork type="json"><![CDATA[ | |
10.5 | |
]]></artwork></figure> | |
<t>and</t> | |
<figure><artwork type="json"><![CDATA[ | |
127 | |
]]></artwork></figure> | |
<t>but rejects</t> | |
<figure><artwork type="json"><![CDATA[ | |
false | |
]]></artwork></figure> | |
<t>The schema:</t> | |
<figure><artwork type="json"><![CDATA[ | |
{"type": "string"} | |
]]></artwork></figure> | |
<t>accepts</t> | |
<figure><artwork type="json"><![CDATA[ | |
"1985-04-12T23:20:50.52Z" | |
]]></artwork></figure> | |
<t>and</t> | |
<figure><artwork type="json"><![CDATA[ | |
"foo" | |
]]></artwork></figure> | |
<t>but rejects</t> | |
<figure><artwork type="json"><![CDATA[ | |
false | |
]]></artwork></figure> | |
<t>The schema:</t> | |
<figure><artwork type="json"><![CDATA[ | |
{"type": "timestamp"} | |
]]></artwork></figure> | |
<t>accepts</t> | |
<figure><artwork type="json"><![CDATA[ | |
"1985-04-12T23:20:50.52Z" | |
]]></artwork></figure> | |
<t>but rejects</t> | |
<figure><artwork type="json"><![CDATA[ | |
"foo" | |
]]></artwork></figure> | |
<t>and</t> | |
<figure><artwork type="json"><![CDATA[ | |
false | |
]]></artwork></figure> | |
<t>The schema:</t> | |
<figure><artwork type="json"><![CDATA[ | |
{"type": "boolean", "nullable": true} | |
]]></artwork></figure> | |
<t>accepts</t> | |
<figure><artwork type="json"><![CDATA[ | |
null | |
]]></artwork></figure> | |
<t>and</t> | |
<figure><artwork type="json"><![CDATA[ | |
false | |
]]></artwork></figure> | |
<t>but rejects</t> | |
<figure><artwork type="json"><![CDATA[ | |
127 | |
]]></artwork></figure> | |
<t>In all of the examples of rejected instances given in this section, the error | |
indicator to produce is:</t> | |
<figure><artwork type="json"><![CDATA[ | |
[{ "instancePath": "", "schemaPath": "/type" }] | |
]]></artwork></figure> | |
</section> | |
<section anchor="semantics-form-enum" title="Enum"> | |
<t>The <spanx style="verb">enum</spanx> form is meant to describe instances whose value must be one of a | |
given set of string values. The syntax of the <spanx style="verb">enum</spanx> form is described in | |
<xref target="syntax-form-enum"/>.</t> | |
<t>If a schema is of the enum form, then:</t> | |
<t><list style="symbols"> | |
<t>If the schema has a member named <spanx style="verb">nullable</spanx> whose value is the boolean <spanx style="verb">true</spanx>, | |
and the instance is the JSON primitive value <spanx style="verb">null</spanx>, then the schema accepts | |
the instance. Otherwise:</t> | |
<t>Let <spanx style="emph">E</spanx> be the value of the schema member with the name <spanx style="verb">enum</spanx>. The instance | |
is accepted if and only if it is equal to one of the elements of <spanx style="emph">E</spanx>.</t> | |
</list></t> | |
<t>If the instance is not accepted, then the error indicator for this case shall | |
have an <spanx style="verb">instancePath</spanx> pointing to the instance, and a <spanx style="verb">schemaPath</spanx> pointing to | |
the schema member with the name <spanx style="verb">enum</spanx>.</t> | |
<t>For example, the schema:</t> | |
<figure><artwork type="json"><![CDATA[ | |
{ "enum": ["PENDING", "DONE", "CANCELED"] } | |
]]></artwork></figure> | |
<t>Accepts</t> | |
<figure><artwork type="json"><![CDATA[ | |
"PENDING" | |
]]></artwork></figure> | |
<t>and</t> | |
<figure><artwork type="json"><![CDATA[ | |
"DONE" | |
]]></artwork></figure> | |
<t>and</t> | |
<figure><artwork type="json"><![CDATA[ | |
"CANCELED" | |
]]></artwork></figure> | |
<t>but rejects all of</t> | |
<figure><artwork type="json"><![CDATA[ | |
0 | |
]]></artwork></figure> | |
<t>and</t> | |
<figure><artwork type="json"><![CDATA[ | |
1 | |
]]></artwork></figure> | |
<t>and</t> | |
<figure><artwork type="json"><![CDATA[ | |
2 | |
]]></artwork></figure> | |
<t>and</t> | |
<figure><artwork type="json"><![CDATA[ | |
"UNKNOWN" | |
]]></artwork></figure> | |
<t>and</t> | |
<figure><artwork type="json"><![CDATA[ | |
null | |
]]></artwork></figure> | |
<t>with the error indicator:</t> | |
<figure><artwork type="json"><![CDATA[ | |
[{ "instancePath": "", "schemaPath": "/enum" }] | |
]]></artwork></figure> | |
<t>The schema</t> | |
<figure><artwork type="json"><![CDATA[ | |
{ "enum": ["PENDING", "DONE", "CANCELED"], "nullable": true } | |
]]></artwork></figure> | |
<t>accepts</t> | |
<figure><artwork type="json"><![CDATA[ | |
"PENDING" | |
]]></artwork></figure> | |
<t>and</t> | |
<figure><artwork type="json"><![CDATA[ | |
null | |
]]></artwork></figure> | |
<t>but rejects</t> | |
<figure><artwork type="json"><![CDATA[ | |
1 | |
]]></artwork></figure> | |
<t>and</t> | |
<figure><artwork type="json"><![CDATA[ | |
"UNKNOWN" | |
]]></artwork></figure> | |
<t>with the error indicator:</t> | |
<figure><artwork type="json"><![CDATA[ | |
[{ "instancePath": "", "schemaPath": "/enum" }] | |
]]></artwork></figure> | |
</section> | |
<section anchor="semantics-form-elements" title="Elements"> | |
<t>The <spanx style="verb">elements</spanx> form is meant to describe instances that must be arrays. A | |
further sub-schema describes the elements of the array. The syntax of the | |
<spanx style="verb">elements</spanx> form is described in <xref target="syntax-form-elements"/>.</t> | |
<t>If a schema is of the elements form, then:</t> | |
<t><list style="symbols"> | |
<t>If the schema has a member named <spanx style="verb">nullable</spanx> whose value is the boolean <spanx style="verb">true</spanx>, | |
and the instance is the JSON primitive value <spanx style="verb">null</spanx>, then the schema accepts | |
the instance. Otherwise:</t> | |
<t>Let <spanx style="emph">S</spanx> be the value of the schema member with the name <spanx style="verb">elements</spanx>. The | |
instance is accepted if and only if all of the following are true: <list style="symbols"> | |
<t>The instance is an array. Otherwise, the error indicator for this case shall | |
have an <spanx style="verb">instancePath</spanx> pointing to the instance, and a <spanx style="verb">schemaPath</spanx> pointing | |
to the schema member with the name <spanx style="verb">elements</spanx>.</t> | |
<t>If the instance is an array, then every element of the instance must be | |
accepted by <spanx style="emph">S</spanx>. Otherwise, the error indicators for this case are the union | |
of all the errors arising from evaluating <spanx style="emph">S</spanx> against elements of the | |
instance.</t> | |
</list></t> | |
</list></t> | |
<t>For example, the schema:</t> | |
<figure><artwork type="json"><![CDATA[ | |
{ | |
"elements": { | |
"type": "float32" | |
} | |
} | |
]]></artwork></figure> | |
<t>accepts</t> | |
<figure><artwork type="json"><![CDATA[ | |
[] | |
]]></artwork></figure> | |
<t>and</t> | |
<figure><artwork type="json"><![CDATA[ | |
[1, 2, 3] | |
]]></artwork></figure> | |
<t>but rejects</t> | |
<figure><artwork type="json"><![CDATA[ | |
null | |
]]></artwork></figure> | |
<t>with the error indicator:</t> | |
<figure><artwork type="json"><![CDATA[ | |
[{ "instancePath": "", "schemaPath": "/elements" }] | |
]]></artwork></figure> | |
<t>and rejects</t> | |
<figure><artwork type="json"><![CDATA[ | |
[1, 2, "foo", 3, "bar"] | |
]]></artwork></figure> | |
<t>with the error indicators:</t> | |
<figure><artwork type="json"><![CDATA[ | |
[ | |
{ "instancePath": "/2", "schemaPath": "/elements/type" }, | |
{ "instancePath": "/4", "schemaPath": "/elements/type" } | |
] | |
]]></artwork></figure> | |
<t>The schema</t> | |
<figure><artwork type="json"><![CDATA[ | |
{ | |
"elements": { | |
"type": "float32" | |
}, | |
"nullable": true | |
} | |
]]></artwork></figure> | |
<t>accepts</t> | |
<figure><artwork type="json"><![CDATA[ | |
null | |
]]></artwork></figure> | |
<t>and</t> | |
<figure><artwork type="json"><![CDATA[ | |
[] | |
]]></artwork></figure> | |
<t>and</t> | |
<figure><artwork type="json"><![CDATA[ | |
[1, 2, 3] | |
]]></artwork></figure> | |
<t>but rejects</t> | |
<figure><artwork type="json"><![CDATA[ | |
[1, 2, "foo", 3, "bar"] | |
]]></artwork></figure> | |
<t>with the error indicators:</t> | |
<figure><artwork type="json"><![CDATA[ | |
[ | |
{ "instancePath": "/2", "schemaPath": "/elements/type" }, | |
{ "instancePath": "/4", "schemaPath": "/elements/type" } | |
] | |
]]></artwork></figure> | |
</section> | |
<section anchor="semantics-form-props" title="Properties"> | |
<t>The <spanx style="verb">properties</spanx> form is meant to describe JSON objects being used as a | |
“struct”. The syntax of the <spanx style="verb">properties</spanx> form is described in | |
<xref target="syntax-form-properties"/>.</t> | |
<t>If a schema is of the properties form, then:</t> | |
<t><list style="symbols"> | |
<t>If the schema has a member named <spanx style="verb">nullable</spanx> whose value is the boolean <spanx style="verb">true</spanx>, | |
and the instance is the JSON primitive value <spanx style="verb">null</spanx>, then the schema accepts | |
the instance. Otherwise the instance is accepted if and only if all of the | |
following are true:</t> | |
<t>The instance is an object. <vspace blankLines='1'/> | |
Otherwise, the error indicator for this case shall have an <spanx style="verb">instancePath</spanx> | |
pointing to the instance, and a <spanx style="verb">schemaPath</spanx> pointing to the schema member | |
with the name <spanx style="verb">properties</spanx> if such a schema member exists; if such a member | |
doesn’t exist, <spanx style="verb">schemaPath</spanx> shall point to the schema member with the name | |
<spanx style="verb">optionalProperties</spanx>.</t> | |
<t>If the instance is an object and the schema has a member named <spanx style="verb">properties</spanx>, | |
then let <spanx style="emph">P</spanx> be the value of the schema member named <spanx style="verb">properties</spanx>. <spanx style="emph">P</spanx>, by | |
<xref target="syntax-form-properties"/>, must be an object. For every member name in <spanx style="emph">P</spanx>, a | |
member of the same name in the instance must exist. <vspace blankLines='1'/> | |
Otherwise, the error indicator for this case shall have an <spanx style="verb">instancePath</spanx> | |
pointing to the instance, and a <spanx style="verb">schemaPath</spanx> pointing to the member of <spanx style="emph">P</spanx> | |
failing the requirement just described.</t> | |
<t>If the instance is an object, then let <spanx style="emph">P</spanx> be the value of the schema member | |
named <spanx style="verb">properties</spanx> (if it exists), and <spanx style="emph">O</spanx> be the value of the schema member | |
named <spanx style="verb">optionalProperties</spanx> (if it exists). <vspace blankLines='1'/> | |
For every member <spanx style="emph">I</spanx> of the instance, find a member with the same name as | |
<spanx style="emph">I</spanx>’s in <spanx style="emph">P</spanx> or <spanx style="emph">O</spanx>. By <xref target="syntax-form-properties"/>, it is not possible for | |
both <spanx style="emph">P</spanx> and <spanx style="emph">O</spanx> to have such a member. If the “discriminator tag exemption” | |
is in effect on <spanx style="emph">I</spanx> (see <xref target="semantics-form-discriminator"/>), then ignore <spanx style="emph">I</spanx>. | |
Otherwise: <list style="symbols"> | |
<t>If no such member in <spanx style="emph">P</spanx> or <spanx style="emph">O</spanx> exists and validation is not in “allow | |
additional properties” mode (see <xref target="allow-additional-properties"/>), then the | |
instance is rejected. <vspace blankLines='1'/> | |
The error indicator for this case has an <spanx style="verb">instancePath</spanx> pointing to <spanx style="emph">I</spanx>, and | |
a <spanx style="verb">schemaPath</spanx> pointing to the schema.</t> | |
<t>If such a member in <spanx style="emph">P</spanx> or <spanx style="emph">O</spanx> does exist, then call this member <spanx style="emph">S</spanx>. If <spanx style="emph">S</spanx> | |
rejects <spanx style="emph">I</spanx>’s value, then the instance is rejected. <vspace blankLines='1'/> | |
The error indicators for this case are the union of the error indicators | |
from evaluating <spanx style="emph">S</spanx> against <spanx style="emph">I</spanx>’s value.</t> | |
</list></t> | |
</list></t> | |
<t>An instance may have multiple errors arising from the third and fourth bullet in | |
the above. In this case, the error indicators are the union of the errors.</t> | |
<t>For example, the schema:</t> | |
<figure><artwork type="json"><![CDATA[ | |
{ | |
"properties": { | |
"a": { "type": "string" }, | |
"b": { "type": "string" } | |
}, | |
"optionalProperties": { | |
"c": { "type": "string" }, | |
"d": { "type": "string" } | |
} | |
} | |
]]></artwork></figure> | |
<t>accepts</t> | |
<figure><artwork type="json"><![CDATA[ | |
{ "a": "foo", "b": "bar" } | |
]]></artwork></figure> | |
<t>and</t> | |
<figure><artwork type="json"><![CDATA[ | |
{ "a": "foo", "b": "bar", "c": "baz" } | |
]]></artwork></figure> | |
<t>and</t> | |
<figure><artwork type="json"><![CDATA[ | |
{ "a": "foo", "b": "bar", "c": "baz", "d": "quux" } | |
]]></artwork></figure> | |
<t>and</t> | |
<figure><artwork type="json"><![CDATA[ | |
{ "a": "foo", "b": "bar", "d": "quux" } | |
]]></artwork></figure> | |
<t>but rejects</t> | |
<figure><artwork type="json"><![CDATA[ | |
null | |
]]></artwork></figure> | |
<t>with the error indicator</t> | |
<figure><artwork type="json"><![CDATA[ | |
[{ "instancePath": "", "schemaPath": "/properties" }] | |
]]></artwork></figure> | |
<t>and rejects</t> | |
<figure><artwork type="json"><![CDATA[ | |
{ "b": 3, "c": 3, "e": 3 } | |
]]></artwork></figure> | |
<t>with the error indicators</t> | |
<figure><artwork type="json"><![CDATA[ | |
[ | |
{ "instancePath": "", | |
"schemaPath": "/properties/a" }, | |
{ "instancePath": "/b", | |
"schemaPath": "/properties/b/type" }, | |
{ "instancePath": "/c", | |
"schemaPath": "/optionalProperties/c/type" }, | |
{ "instancePath": "/e", | |
"schemaPath": "" } | |
] | |
]]></artwork></figure> | |
<t>If instead the schema had <spanx style="verb">additionalProperties: true</spanx>, but was otherwise the | |
same:</t> | |
<figure><artwork type="json"><![CDATA[ | |
{ | |
"properties": { | |
"a": { "type": "string" }, | |
"b": { "type": "string" } | |
}, | |
"optionalProperties": { | |
"c": { "type": "string" }, | |
"d": { "type": "string" } | |
}, | |
"additionalProperties": true | |
} | |
]]></artwork></figure> | |
<t>And the instance remained the same:</t> | |
<figure><artwork type="json"><![CDATA[ | |
{ "b": 3, "c": 3, "e": 3 } | |
]]></artwork></figure> | |
<t>Then the error indicators from evaluating the instance against the schema would | |
be:</t> | |
<figure><artwork type="json"><![CDATA[ | |
[ | |
{ "instancePath": "", | |
"schemaPath": "/properties/a" }, | |
{ "instancePath": "/b", | |
"schemaPath": "/properties/b/type" }, | |
{ "instancePath": "/c", | |
"schemaPath": "/optionalProperties/c/type" }, | |
] | |
]]></artwork></figure> | |
<t>These are the same errors as before, except the final error (associated with the | |
additional member named <spanx style="verb">e</spanx> in the instance) is no longer present. This is | |
because <spanx style="verb">additionalProperties: true</spanx> enables “allow additional properties” mode | |
on the schema.</t> | |
<t>Finally, the schema:</t> | |
<figure><artwork type="json"><![CDATA[ | |
{ | |
"nullable": true, | |
"properties": { | |
"a": { "type": "string" }, | |
"b": { "type": "string" } | |
}, | |
"optionalProperties": { | |
"c": { "type": "string" }, | |
"d": { "type": "string" } | |
}, | |
"additionalProperties": true | |
} | |
]]></artwork></figure> | |
<t>accepts</t> | |
<figure><artwork type="json"><![CDATA[ | |
null | |
]]></artwork></figure> | |
<t>but rejects</t> | |
<figure><artwork type="json"><![CDATA[ | |
{ "b": 3, "c": 3, "e": 3 } | |
]]></artwork></figure> | |
<t>with the error indicators</t> | |
<figure><artwork type="json"><![CDATA[ | |
[ | |
{ "instancePath": "", | |
"schemaPath": "/properties/a" }, | |
{ "instancePath": "/b", | |
"schemaPath": "/properties/b/type" }, | |
{ "instancePath": "/c", | |
"schemaPath": "/optionalProperties/c/type" }, | |
] | |
]]></artwork></figure> | |
</section> | |
<section anchor="semantics-form-values" title="Values"> | |
<t>The <spanx style="verb">values</spanx> form is meant to describe instances that are JSON objects being | |
used as an associative array. The syntax of the <spanx style="verb">values</spanx> form is described in | |
<xref target="syntax-form-values"/>.</t> | |
<t>If a schema is of the values form, then:</t> | |
<t><list style="symbols"> | |
<t>If the schema has a member named <spanx style="verb">nullable</spanx> whose value is the boolean <spanx style="verb">true</spanx>, | |
and the instance is the JSON primitive value <spanx style="verb">null</spanx>, then the schema accepts | |
the instance. Otherwise:</t> | |
<t>Let <spanx style="emph">S</spanx> be the value of the schema member with the name <spanx style="verb">values</spanx>. The instance | |
is accepted if and only if all of the following are true: <list style="symbols"> | |
<t>The instance is an object. Otherwise, the error indicator for this case | |
shall have an <spanx style="verb">instancePath</spanx> pointing to the instance, and a <spanx style="verb">schemaPath</spanx> | |
pointing to the schema member with the name <spanx style="verb">values</spanx>.</t> | |
<t>If the instance is an object, then every member value of the instance must | |
be accepted by <spanx style="emph">S</spanx>. Otherwise, the error indicators for this case are the | |
union of all the error indicators arising from evaluating <spanx style="emph">S</spanx> against member | |
values of the instance.</t> | |
</list></t> | |
</list></t> | |
<t>For example, the schema:</t> | |
<figure><artwork type="json"><![CDATA[ | |
{ | |
"values": { | |
"type": "float32" | |
} | |
} | |
]]></artwork></figure> | |
<t>accepts</t> | |
<figure><artwork type="json"><![CDATA[ | |
{} | |
]]></artwork></figure> | |
<t>and</t> | |
<figure><artwork type="json"><![CDATA[ | |
{"a": 1, "b": 2} | |
]]></artwork></figure> | |
<t>but rejects</t> | |
<figure><artwork type="json"><![CDATA[ | |
null | |
]]></artwork></figure> | |
<t>with the error indicator</t> | |
<figure><artwork type="json"><![CDATA[ | |
[{ "instancePath": "", "schemaPath": "/values" }] | |
]]></artwork></figure> | |
<t>and rejects</t> | |
<figure><artwork type="json"><![CDATA[ | |
{ "a": 1, "b": 2, "c": "foo", "d": 3, "e": "bar" } | |
]]></artwork></figure> | |
<t>with the error indicators</t> | |
<figure><artwork type="json"><![CDATA[ | |
[ | |
{ "instancePath": "/c", "schemaPath": "/values/type" }, | |
{ "instancePath": "/e", "schemaPath": "/values/type" } | |
] | |
]]></artwork></figure> | |
<t>The schema:</t> | |
<figure><artwork type="json"><![CDATA[ | |
{ | |
"nullable": true, | |
"values": { | |
"type": "float32" | |
} | |
} | |
]]></artwork></figure> | |
<t>accepts</t> | |
<figure><artwork type="json"><![CDATA[ | |
null | |
]]></artwork></figure> | |
<t>but rejects</t> | |
<figure><artwork type="json"><![CDATA[ | |
{ "a": 1, "b": 2, "c": "foo", "d": 3, "e": "bar" } | |
]]></artwork></figure> | |
<t>with the error indicators</t> | |
<figure><artwork type="json"><![CDATA[ | |
[ | |
{ "instancePath": "/c", "schemaPath": "/values/type" }, | |
{ "instancePath": "/e", "schemaPath": "/values/type" } | |
] | |
]]></artwork></figure> | |
</section> | |
<section anchor="semantics-form-discriminator" title="Discriminator"> | |
<t>The <spanx style="verb">discriminator</spanx> 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 <spanx style="verb">discriminator</spanx> form is described in | |
<xref target="syntax-form-discriminator"/>.</t> | |
<t>When a schema is of the “discriminator” form, it validates:</t> | |
<t><list style="symbols"> | |
<t>That the instance is an object,</t> | |
<t>That the instance has a particular “tag” property,</t> | |
<t>That this “tag” property’s value is a string within a set of valid values, and</t> | |
<t>That the instance satisfies another schema, where this other schema is chosen | |
based on the value of the “tag” property.</t> | |
</list></t> | |
<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"/> 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> | |
<t><list style="symbols"> | |
<t>Let <spanx style="emph">D</spanx> be the schema member with the name <spanx style="verb">discriminator</spanx>.</t> | |
<t>Let <spanx style="emph">M</spanx> be the schema member with the name <spanx style="verb">mapping</spanx>.</t> | |
<t>Let <spanx style="emph">I</spanx> be the instance member whose name equals <spanx style="emph">D</spanx>’s value. <spanx style="emph">I</spanx> may, for | |
some rejected instances, not exist.</t> | |
<t>Let <spanx style="emph">S</spanx> be the member of <spanx style="emph">M</spanx> whose name equals <spanx style="emph">I</spanx>’s value. <spanx style="emph">S</spanx> may, for some | |
rejected instances, not exist.</t> | |
</list></t> | |
<t>If the schema has a member named <spanx style="verb">nullable</spanx> whose value is the boolean <spanx style="verb">true</spanx>, | |
and the instance is the JSON primitive value <spanx style="verb">null</spanx>, then the schema accepts the | |
instance. Otherwise the instance is accepted if and only if all of the following | |
are true:</t> | |
<t><list style="symbols"> | |
<t>The instance is an object. <vspace blankLines='1'/> | |
Otherwise, the error indicator for this case shall have an <spanx style="verb">instancePath</spanx> | |
pointing to the instance, and a <spanx style="verb">schemaPath</spanx> pointing to <spanx style="emph">D</spanx>.</t> | |
<t>If the instance is a JSON object, then <spanx style="emph">I</spanx> must exist. <vspace blankLines='1'/> | |
Otherwise, the error indicator for this case shall have an <spanx style="verb">instancePath</spanx> | |
pointing to the instance, and a <spanx style="verb">schemaPath</spanx> pointing to <spanx style="emph">D</spanx>.</t> | |
<t>If the instance is a JSON object and <spanx style="emph">I</spanx> exists, <spanx style="emph">I</spanx>’s value must be a string. <vspace blankLines='1'/> | |
Otherwise, the error indicator for this case shall have an <spanx style="verb">instancePath</spanx> | |
pointing to <spanx style="emph">I</spanx>, and a <spanx style="verb">schemaPath</spanx> pointing to <spanx style="emph">D</spanx>.</t> | |
<t>If the instance is a JSON object and <spanx style="emph">I</spanx> exists and has a string value, then | |
<spanx style="emph">S</spanx> must exist. <vspace blankLines='1'/> | |
Otherwise, the error indicator for this case shall have an <spanx style="verb">instancePath</spanx> | |
pointing to <spanx style="emph">I</spanx>, and a <spanx style="verb">schemaPath</spanx> pointing to <spanx style="emph">M</spanx>.</t> | |
<t>If the instance is a JSON object, <spanx style="emph">I</spanx> exists, and <spanx style="emph">S</spanx> exists, then the | |
instance must satisfy <spanx style="emph">S</spanx>’s value. By <xref target="syntax"/>, <spanx style="emph">S</spanx>’s value must be a schema | |
of the properties form. Apply the “discriminator tag exemption” afforded in | |
<xref target="semantics-form-props"/> to <spanx style="emph">I</spanx> when evaluating whether the instance satisfies | |
<spanx style="emph">S</spanx>’s value. <vspace blankLines='1'/> | |
Otherwise, the error indicators for this case shall be error indicators from | |
evaluating <spanx style="emph">S</spanx>’s value against the instance, with the “discriminator tag | |
exemption” applied to <spanx style="emph">I</spanx>.</t> | |
</list></t> | |
<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> | |
<figure><artwork type="json"><![CDATA[ | |
{ | |
"discriminator": "version", | |
"mapping": { | |
"v1": { | |
"properties": { | |
"a": { "type": "float32" } | |
} | |
}, | |
"v2": { | |
"properties": { | |
"a": { "type": "string" } | |
} | |
} | |
} | |
} | |
]]></artwork></figure> | |
<t>rejects</t> | |
<figure><artwork type="json"><![CDATA[ | |
null | |
]]></artwork></figure> | |
<t>with the error indicator</t> | |
<figure><artwork type="json"><![CDATA[ | |
[{ "instancePath": "", "schemaPath": "/discriminator" }] | |
]]></artwork></figure> | |
<t>(This is the case of the instance not being an object.)</t> | |
<t>Also rejected is</t> | |
<figure><artwork type="json"><![CDATA[ | |
{} | |
]]></artwork></figure> | |
<t>with the error indicator</t> | |
<figure><artwork type="json"><![CDATA[ | |
[{ "instancePath": "", "schemaPath": "/discriminator" }] | |
]]></artwork></figure> | |
<t>(This is the case of <spanx style="emph">I</spanx> not existing.)</t> | |
<t>Also rejected is</t> | |
<figure><artwork type="json"><![CDATA[ | |
{ "version": 1 } | |
]]></artwork></figure> | |
<t>with the error indicator</t> | |
<figure><artwork type="json"><![CDATA[ | |
[ | |
{ | |
"instancePath": "/version", | |
"schemaPath": "/discriminator" | |
} | |
] | |
]]></artwork></figure> | |
<t>(This is the case of <spanx style="emph">I</spanx> existing, but not having a string value.)</t> | |
<t>Also rejected is</t> | |
<figure><artwork type="json"><![CDATA[ | |
{ "version": "v3" } | |
]]></artwork></figure> | |
<t>with the error indicator</t> | |
<figure><artwork type="json"><![CDATA[ | |
[ | |
{ | |
"instancePath": "/version", | |
"schemaPath": "/mapping" | |
} | |
] | |
]]></artwork></figure> | |
<t>(This is the case of <spanx style="emph">I</spanx> existing and having a string value, but <spanx style="emph">S</spanx> not | |
existing.)</t> | |
<t>Also rejected is</t> | |
<figure><artwork type="json"><![CDATA[ | |
{ "version": "v2", "a": 3 } | |
]]></artwork></figure> | |
<t>with the error indicator</t> | |
<figure><artwork type="json"><![CDATA[ | |
[ | |
{ | |
"instancePath": "/a", | |
"schemaPath": "/mapping/v2/properties/a/type" | |
} | |
] | |
]]></artwork></figure> | |
<t>(This is the case of <spanx style="emph">I</spanx> and <spanx style="emph">S</spanx> existing, but the instance not satisfying <spanx style="emph">S</spanx>’s | |
value.)</t> | |
<t>Finally, the schema accepts</t> | |
<figure><artwork type="json"><![CDATA[ | |
{ "version": "v2", "a": "foo" } | |
]]></artwork></figure> | |
<t>This instance is accepted even though <spanx style="verb">version</spanx> is not mentioned by | |
<spanx style="verb">/mapping/v2/properties</spanx>; the “discriminator tag exemption” ensures that | |
<spanx style="verb">version</spanx> is not treated as an additional property when evaluating the instance | |
against <spanx style="emph">S</spanx>’s value.</t> | |
<t>By contrast, consider the same schema, but with <spanx style="verb">nullable</spanx> being <spanx style="verb">true</spanx>. The | |
schema:</t> | |
<figure><artwork type="json"><![CDATA[ | |
{ | |
"nullable": true, | |
"discriminator": "version", | |
"mapping": { | |
"v1": { | |
"properties": { | |
"a": { "type": "float32" } | |
} | |
}, | |
"v2": { | |
"properties": { | |
"a": { "type": "string" } | |
} | |
} | |
} | |
} | |
]]></artwork></figure> | |
<t>accepts</t> | |
<figure><artwork type="json"><![CDATA[ | |
null | |
]]></artwork></figure> | |
<t>To further illustrate the discriminator form with examples, recall the JTD | |
schema in <xref target="syntax-form-discriminator"/>, reproduced here:</t> | |
<figure><artwork type="json"><![CDATA[ | |
{ | |
"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" } | |
} | |
} | |
} | |
} | |
]]></artwork></figure> | |
<t>This schema accepts</t> | |
<figure><artwork type="json"><![CDATA[ | |
{ "event_type": "account_deleted", "account_id": "abc-123" } | |
]]></artwork></figure> | |
<t>and</t> | |
<figure><artwork type="json"><![CDATA[ | |
{ | |
"event_type": "account_payment_plan_changed", | |
"account_id": "abc-123", | |
"payment_plan": "PAID" | |
} | |
]]></artwork></figure> | |
<t>and</t> | |
<figure><artwork type="json"><![CDATA[ | |
{ | |
"event_type": "account_payment_plan_changed", | |
"account_id": "abc-123", | |
"payment_plan": "PAID", | |
"upgraded_by": "users/mkhwarizmi" | |
} | |
]]></artwork></figure> | |
<t>but rejects</t> | |
<figure><artwork type="json"><![CDATA[ | |
{} | |
]]></artwork></figure> | |
<t>with the error indicator</t> | |
<figure><artwork type="json"><![CDATA[ | |
[{ "instancePath": "", "schemaPath": "/discriminator" }] | |
]]></artwork></figure> | |
<t>and rejects</t> | |
<figure><artwork type="json"><![CDATA[ | |
{ "event_type": "some_other_event_type" } | |
]]></artwork></figure> | |
<t>with the error indicator</t> | |
<figure><artwork type="json"><![CDATA[ | |
[ | |
{ | |
"instancePath": "/event_type", | |
"schemaPath": "/mapping" | |
} | |
] | |
]]></artwork></figure> | |
<t>and rejects</t> | |
<figure><artwork type="json"><![CDATA[ | |
{ "event_type": "account_deleted" } | |
]]></artwork></figure> | |
<t>with the error indicator</t> | |
<figure><artwork type="json"><![CDATA[ | |
[{ | |
"instancePath": "", | |
"schemaPath": "/mapping/account_deleted/properties/account_id" | |
}] | |
]]></artwork></figure> | |
<t>and rejects</t> | |
<figure><artwork type="json"><![CDATA[ | |
{ | |
"event_type": "account_payment_plan_changed", | |
"account_id": "abc-123", | |
"payment_plan": "PAID", | |
"xxx": "asdf" | |
} | |
]]></artwork></figure> | |
<t>with the error indicator</t> | |
<figure><artwork type="json"><![CDATA[ | |
[{ | |
"instancePath": "/xxx", | |
"schemaPath": "/mapping/account_payment_plan_changed" | |
}] | |
]]></artwork></figure> | |
</section> | |
</section> | |
</section> | |
<section anchor="iana-considerations" title="IANA Considerations"> | |
<t>No IANA considerations.</t> | |
</section> | |
<section anchor="security-considerations" title="Security Considerations"> | |
<t>Implementations of JTD will necessarily be manipulating JSON data. Therefore, | |
the security considerations of <xref target="RFC8259"/> are all relevant here.</t> | |
<t>Implementations which evaluate user-inputted schemas SHOULD implement mechanisms | |
to detect, and abort, circular references which 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> | |
<references title='Normative References'> | |
&RFC3339; | |
&RFC4287; | |
&RFC6901; | |
&RFC8259; | |
&RFC8610; | |
&RFC2119; | |
&RFC8174; | |
</references> | |
<references title='Informative References'> | |
&RFC7071; | |
&RFC7493; | |
&I-D.handrews-json-schema; | |
<reference anchor="OPENAPI" target="https://spec.openapis.org/oas/v3.0.2"> | |
<front> | |
<title>OpenAPI Specification</title> | |
<author > | |
<organization>OpenAPI Initiative</organization> | |
</author> | |
<date year="2019" month="October" day="08"/> | |
</front> | |
</reference> | |
</references> | |
<section anchor="other-considerations" title="Rationale for Omitted Features"> | |
<t>This appendix is not normative.</t> | |
<t>This section describes possible features which are intentionally left out of | |
JSON Type Definition, and justifies why these features are omitted.</t> | |
<section anchor="other-considerations-int64" title="Support for 64-bit Numbers"> | |
<t>This document does not allow <spanx style="verb">int64</spanx> or <spanx style="verb">uint64</spanx> as values for the JTD <spanx style="verb">type</spanx> | |
keyword (see <xref target="syntax-form-type"/> and <xref target="semantics-form-type"/>). Such | |
hypothetical <spanx style="verb">int64</spanx> or <spanx style="verb">uint64</spanx> types would behave like <spanx style="verb">int32</spanx> or <spanx style="verb">uint32</spanx> | |
(respectively), but with the range of values associated with 64-bit instead of | |
32-bit integers, that is:</t> | |
<t><list style="symbols"> | |
<t><spanx style="verb">int64</spanx> would accept numbers between -(2**63) and (2**63)-1</t> | |
<t><spanx style="verb">uint64</spanx> would accept numbers between 0 and (2**64)-1</t> | |
</list></t> | |
<t>Users of <spanx style="verb">int64</spanx> and <spanx style="verb">uint64</spanx> 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 Section 2.2 of <xref target="RFC7493"/>.</t> | |
<t><spanx style="verb">int64</spanx> and <spanx style="verb">uint64</spanx> 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 <spanx style="verb">int64</spanx> and <spanx style="verb">uint64</spanx>.</t> | |
</section> | |
<section anchor="support-for-non-root-definitions" title="Support for Non-Root Definitions"> | |
<t>This document disallows the <spanx style="verb">definitions</spanx> keyword from appearing outside of root | |
schemas (see <xref target="cddl-schema"/>). Conceivably, this document could have instead | |
allowed <spanx style="verb">definitions</spanx> to appear on any schema, even non-root ones. Under this | |
alternative design, <spanx style="verb">ref</spanx>s would resolve to a definition in the “nearest” (i.e., | |
most nested) schema which both contained the <spanx style="verb">ref</spanx> and which had a | |
suitably-named <spanx style="verb">definitions</spanx> member.</t> | |
<t>For instance, under this alternative approach, one could define schemas like the | |
one in <xref target="hypothetical-ref"/>:</t> | |
<figure title="A hypothetical schema had this document permitted | |
non-root definitions. This is not a correct JTD schema." anchor="hypothetical-ref"><artwork type="json"><![CDATA[ | |
{ | |
"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" | |
} | |
} | |
} | |
]]></artwork></figure> | |
<t>If schemas like that in <xref target="hypothetical-ref"/> 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 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 <spanx style="verb">user</spanx> definitions | |
in <xref target="hypothetical-ref"/> might have been generated into types named | |
<spanx style="verb">PropertiesFooUser</spanx>, <spanx style="verb">PropertiesBarUser</spanx>, and <spanx style="verb">PropertiesBazUser</spanx>; obtuse names | |
like these are less useful to human-written code than names like <spanx style="verb">User</spanx>.</t> | |
<t>Furthermore, even though <spanx style="verb">PropertiesFooUser</spanx> and <spanx style="verb">PropertiesBarUser</spanx> 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 <spanx style="verb">PropertiesBazUser</spanx>, defined from a | |
schema allowing a property named <spanx style="verb">userId</spanx> (not <spanx style="verb">user_id</spanx>), 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" title="Comparison with CDDL"> | |
<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 which accept and reject the same | |
instances.</t> | |
<t>The JTD schema:</t> | |
<figure><artwork type="json"><![CDATA[ | |
{} | |
]]></artwork></figure> | |
<t>accepts the same instances as the CDDL rule:</t> | |
<figure><artwork type="cddl"><![CDATA[ | |
root = any | |
]]></artwork></figure> | |
<t>The JTD schema:</t> | |
<figure><artwork type="json"><![CDATA[ | |
{ | |
"definitions": { | |
"a": { "elements": { "ref": "b" }}, | |
"b": { "type": "float32" } | |
}, | |
"elements": { | |
"ref": "a" | |
} | |
} | |
]]></artwork></figure> | |
<t>accepts the same instances as the CDDL rule:</t> | |
<figure><artwork type="cddl"><![CDATA[ | |
root = [* a] | |
a = [* b] | |
b = number | |
]]></artwork></figure> | |
<t>The JTD schema:</t> | |
<figure><artwork type="json"><![CDATA[ | |
{ "enum": ["PENDING", "DONE", "CANCELED"]} | |
]]></artwork></figure> | |
<t>accepts the same instances as the CDDL rule:</t> | |
<figure><artwork type="cddl"><![CDATA[ | |
root = "PENDING" / "DONE" / "CANCELED" | |
]]></artwork></figure> | |
<t>The JTD schema:</t> | |
<figure><artwork type="json"><![CDATA[ | |
{"type": "boolean"} | |
]]></artwork></figure> | |
<t>accepts the same instances as the CDDL rule:</t> | |
<figure><artwork type="cddl"><![CDATA[ | |
root = bool | |
]]></artwork></figure> | |
<t>The JTD schemas:</t> | |
<figure><artwork type="json"><![CDATA[ | |
{"type": "float32"} | |
]]></artwork></figure> | |
<t>and</t> | |
<figure><artwork type="json"><![CDATA[ | |
{"type": "float64"} | |
]]></artwork></figure> | |
<t>both accept the same instances as the CDDL rule:</t> | |
<figure><artwork type="cddl"><![CDATA[ | |
root = number | |
]]></artwork></figure> | |
<t>The JTD schema:</t> | |
<figure><artwork type="json"><![CDATA[ | |
{"type": "string"} | |
]]></artwork></figure> | |
<t>accepts the same instances as the CDDL rule:</t> | |
<figure><artwork type="cddl"><![CDATA[ | |
root = tstr | |
]]></artwork></figure> | |
<t>The JTD schema:</t> | |
<figure><artwork type="json"><![CDATA[ | |
{"type": "timestamp"} | |
]]></artwork></figure> | |
<t>accepts the same instances as the CDDL rule:</t> | |
<figure><artwork type="cddl"><![CDATA[ | |
root = tdate | |
]]></artwork></figure> | |
<t>The JTD schema:</t> | |
<figure><artwork type="json"><![CDATA[ | |
{ "elements": { "type": "float32" }} | |
]]></artwork></figure> | |
<t>accepts the same instances as the CDDL rule:</t> | |
<figure><artwork type="cddl"><![CDATA[ | |
root = [* number] | |
]]></artwork></figure> | |
<t>The JTD schema:</t> | |
<figure><artwork type="json"><![CDATA[ | |
{ | |
"properties": { | |
"a": { "type": "boolean" }, | |
"b": { "type": "float32" } | |
}, | |
"optionalProperties": { | |
"c": { "type": "string" }, | |
"d": { "type": "timestamp" } | |
} | |
} | |
]]></artwork></figure> | |
<t>accepts the same instances as the CDDL rule:</t> | |
<figure><artwork type="cddl"><![CDATA[ | |
root = { a: bool, b: number, ? c: tstr, ? d: tdate } | |
]]></artwork></figure> | |
<t>The JTD schema:</t> | |
<figure><artwork type="json"><![CDATA[ | |
{ "values": { "type": "float32" }} | |
]]></artwork></figure> | |
<t>accepts the same instances as the CDDL rule:</t> | |
<figure><artwork type="cddl"><![CDATA[ | |
root = { * tstr => number } | |
]]></artwork></figure> | |
<t>Finally, the JTD schema:</t> | |
<figure><artwork type="json"><![CDATA[ | |
{ | |
"discriminator": "a", | |
"mapping": { | |
"foo": { | |
"properties": { | |
"b": { "type": "float32" } | |
} | |
}, | |
"bar": { | |
"properties": { | |
"b": { "type": "string" } | |
} | |
} | |
} | |
} | |
]]></artwork></figure> | |
<t>accepts the same instances as the CDDL rule:</t> | |
<figure><artwork type="cddl"><![CDATA[ | |
root = { a: "foo", b: number } / { a: "bar", b: tstr } | |
]]></artwork></figure> | |
</section> | |
<section anchor="examples" title="Example"> | |
<t>This appendix is not normative.</t> | |
<t>As a demonstration of JTD, in <xref target="JTD-reputation-object"/> is a JTD schema | |
closely equivalent to the plain-English definition <spanx style="verb">reputation-object</spanx> described | |
in Section 6.2.2 of <xref target="RFC7071"/>:</t> | |
<figure title="A JTD schema describing "reputation-object" | |
from Section 6.6.2 of [RFC7071]" anchor="JTD-reputation-object"><artwork type="json"><![CDATA[ | |
{ | |
"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" } | |
} | |
} | |
} | |
} | |
} | |
]]></artwork></figure> | |
<t>This schema does not enforce the requirement that <spanx style="verb">sample-size</spanx>, <spanx style="verb">generated</spanx>, | |
and <spanx style="verb">expires</spanx> be unbounded positive integers. It does not express the limitation | |
that <spanx style="verb">rating</spanx>, <spanx style="verb">confidence</spanx>, and <spanx style="verb">normal-rating</spanx> should not have more than three | |
decimal places of precision.</t> | |
<t>The example in <xref target="JTD-reputation-object"/> can be compared against the equivalent | |
example in Appendix H of <xref target="RFC8610"/>.</t> | |
</section> | |
<section numbered="no" anchor="acknowledgments" title="Acknowledgments"> | |
<t>Carsten Bormann provided lots of useful guidance and feedback on JTD’s design | |
and the structure of this document.</t> | |
<t>Evgeny Poberezkin suggested the addition of <spanx style="verb">nullable</spanx>, and thoroughly vetted | |
this document for mistakes and opportunities for simplification.</t> | |
<t>Tim Bray suggested the current <spanx style="verb">ref</spanx> model, and the addition of <spanx style="verb">enum</spanx>. Anders | |
Rundgren suggested extending <spanx style="verb">type</spanx> to have more support for numerical types. | |
James Manger suggested additional clarifying examples of how integer types work. | |
Adrian Farrel suggested many improvements to help make this document clearer.</t> | |
<t>Members of the IETF JSON mailing list – in particular, Pete Cordell, Phillip | |
Hallam-Baker, Nico Williams, John Cowan, Rob Sayre, and Erik Wilde – provided | |
lots of useful feedback.</t> | |
<t>OpenAPI’s <spanx style="verb">discriminator</spanx> object <xref target="OPENAPI"/> inspired the <spanx style="verb">discriminator</spanx> form. | |
<xref target="I-D.handrews-json-schema"/> influenced various parts of JTD’s early design.</t> | |
</section> | |
</back> | |
<!-- ##markdown-source: | |
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 | |
hc0EAiAAm+iIpEM0Vv8PkzDhsur1AAA= | |
--> | |
</rfc> | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment