Skip to content

Commit 8f83b3d

Browse files
authored
Merge pull request #1 from json-structure/add-choice
Introducing choice type
2 parents b0c1472 + ebea75e commit 8f83b3d

File tree

1 file changed

+169
-2
lines changed

1 file changed

+169
-2
lines changed

draft-vasters-json-structure-core.md

Lines changed: 169 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -651,6 +651,137 @@ Example:
651651
}
652652
~~~
653653

654+
#### `choice` {#choice}
655+
656+
The `choice` type is used to define a "discriminated union" of types. A
657+
choice is a set of types where only one type can be selected at a time and
658+
where the selected type is determined by the value of a selector.
659+
660+
The `choice` type can declare two variants of discriminated unions that
661+
are represented differently in JSON:
662+
663+
- _Tagged unions_: The `choice` type is represented as a JSON object with a
664+
single property whose name is the selector of the type as declared in the
665+
[`choices`]({{choices-keyword}}) map and whose value is of the selected type.
666+
- _Inline unions_: The `choice` type is represented as a JSON object of the
667+
selected type with the selector as a property of the object.
668+
669+
##### Tagged Unions {#tagged-unions}
670+
671+
A tagged union is declared as follows:
672+
673+
~~~ json
674+
{
675+
"type": "choice",
676+
"name": "MyChoice",
677+
"choices": {
678+
"string": { "type": "string" },
679+
"int32": { "type": "int32" }
680+
}
681+
}
682+
~~~
683+
684+
The JSON node described by the schema above is a tagged union. For the
685+
example, the following JSON node is a valid instance of the `MyChoice` type:
686+
687+
~~~ json
688+
{
689+
"string": "Hello, world!"
690+
}
691+
~~~
692+
693+
or:
694+
695+
~~~ json
696+
{
697+
"int32": 42
698+
}
699+
700+
##### Inline Unions {#inline-unions}
701+
702+
Inline unions require for all type choices to extend a common base type.
703+
704+
This is expressed by using the [`$extends`]({{extends}}) keyword in the
705+
`choice` declaration. The `$extends` keyword MUST refer to a schema that
706+
defines the base type and the base type MUST be abstract.
707+
708+
If `$extends` is present, the `selector` property declares the name of the
709+
injected property that acts as the selector for the inline union. The
710+
type of the selector property is `string`. The selector property MAY
711+
shadow a property of the base type; in this case, the base type property
712+
MUST be of type `string`.
713+
714+
The selector is defined as a property of the base type and the value of the
715+
selector property MUST be a string that matches the name of one of the
716+
options in the `choices` map.
717+
718+
Example:
719+
720+
~~~ json
721+
{
722+
"$schema": "https://json-structure.org/meta/core/v0/#",
723+
"$id": "https://schemas.vasters.com/TypeName",
724+
"type": "choice",
725+
"$extends": "#/definitions/Address",
726+
"selector": "addressType",
727+
"choices": {
728+
"StreetAddress": { "$ref": "#/definitions/StreetAddress" },
729+
"PostOfficeBoxAddress": { "$ref": "#/definitions/PostOfficeBoxAddress" }
730+
},
731+
"definitions" : {
732+
"Address": {
733+
"abstract": true,
734+
"type": "object",
735+
"properties": {
736+
"city": { "type": "string" },
737+
"state": { "type": "string" },
738+
"zip": { "type": "string" }
739+
}
740+
},
741+
"StreetAddress": {
742+
"type": "object",
743+
"$extends": "#/definitions/Address",
744+
"properties": {
745+
"street": { "type": "string" }
746+
}
747+
},
748+
"PostOfficeBoxAddress": {
749+
"type": "object",
750+
"$extends": "#/definitions/Address",
751+
"properties": {
752+
"poBox": { "type": "string" }
753+
}
754+
}
755+
}
756+
}
757+
~~~
758+
759+
The JSON node described by the schema above is an inline union. This
760+
example shows a JSON node that is a street address:
761+
762+
~~~ json
763+
{
764+
"addressType": "StreetAddress",
765+
"street": "123 Main St",
766+
"city": "Seattle",
767+
"state": "WA",
768+
"zip": "98101"
769+
}
770+
~~~
771+
772+
This example shows a JSON node that is a post office box address:
773+
774+
~~~ json
775+
{
776+
"addressType": "PostOfficeBoxAddress",
777+
"poBox": "1234",
778+
"city": "Seattle",
779+
"state": "WA",
780+
"zip": "98101"
781+
}
782+
~~~
783+
784+
654785
## Document Structure {#document-structure}
655786

656787
A JSON Structure document is a JSON object that contains schemas ({{schema}})
@@ -973,8 +1104,10 @@ specification.
9731104

9741105
### Unions {#unions}
9751106

976-
- Type unions are formed as sets of primitive types and type references. It is
977-
NOT permitted to define a compound type inline inside a union.
1107+
- Non-discriminated type unions are formed as sets of primitive types and type
1108+
references. It is NOT permitted to define a compound type inline inside a
1109+
non-discriminated type union. Discriminated unions are formed as a
1110+
[`choice`]({{choice}}) type to which the rules of this section do not apply.
9781111
- A type union is a composite type reference and not a standalone compound type
9791112
and is therefore not named.
9801113
- The JSON node described by a schema with a type union MUST conform to at least
@@ -1206,6 +1339,38 @@ provided with a schema, each additional property MUST conform to it.
12061339
}
12071340
~~~
12081341

1342+
### The `choices` Keyword {#choices-keyword}
1343+
1344+
`choices` defines the choices of a `choice` type. The value MUST be a map of
1345+
type names to schemas. Each type name MUST be unique within the `choices` map.
1346+
1347+
The value of each type name MUST be a schema. Inline compound types are permitted.
1348+
1349+
The `choices` keyword MUST only be used in schemas of type [`choice`]({{choice}}).
1350+
1351+
***Example**:
1352+
1353+
~~~ json
1354+
{
1355+
"type": "choice",
1356+
"name": "MyChoice",
1357+
"choices": {
1358+
"string": { "type": "string" },
1359+
"int32": { "type": "int32" }
1360+
}
1361+
}
1362+
~~~
1363+
1364+
1365+
### The `selector` Keyword {#selector-keyword}
1366+
1367+
The `selector` keyword defines the name of the property that acts as the selector
1368+
for the type in a `choice` type. The value of `selector` MUST be a string.
1369+
1370+
The `selector` keyword MUST only be used in schemas of type [`choice`]({{choice}}).
1371+
1372+
See [`choice`]({{choice}}) for an example.
1373+
12091374
## Type Annotation Keywords {#type-annotation-keywords}
12101375

12111376
Type annotation keywords provide additional metadata about the underlying type.
@@ -1552,6 +1717,7 @@ custom annotations or extension keywords:
15521717
- `$offers`
15531718
- `abstract`
15541719
- `additionalProperties`
1720+
- `choices`
15551721
- `const`
15561722
- `default`
15571723
- `description`
@@ -1565,6 +1731,7 @@ custom annotations or extension keywords:
15651731
- `properties`
15661732
- `required`
15671733
- `scale`
1734+
- `selector`
15681735
- `type`
15691736
- `values`
15701737

0 commit comments

Comments
 (0)