mirror of https://github.com/kdl-org/kdl.git
Editorial rephrasing; define empty array/object
Did an editorial pass over the document, rewriting most of the prose slightly. Caught a place or two that was still referring to tags for object keys. Sole non-editorial change was adding the final ambiguous case - a completely empty node. These are required to have an `(array)` or `(object)` tag to be valid.
This commit is contained in:
parent
fb80016011
commit
76d5dd542a
117
JSON-IN-KDL.md
117
JSON-IN-KDL.md
|
|
@ -3,23 +3,24 @@ JSON-in-KDL (JiK)
|
|||
|
||||
This specification describes a canonical way to losslessly encode [JSON](https://json.org) in [KDL](https://kdl.dev). While this isn't a very useful thing to want to do on its own, it's occasionally useful when using a KDL toolchain while speaking with a JSON-consuming or -emitting service.
|
||||
|
||||
This is version 3.0.0 of JiK.
|
||||
This is version 3.0.1 of JiK.
|
||||
|
||||
JSON-in-KDL (JiK from now on) is a kdl microsyntax consisting of named nodes that represent objects, arrays, or literal values.
|
||||
|
||||
The name "-" is used for nodes that are nameless, i.e. the top-level node and items in an array.
|
||||
----
|
||||
|
||||
JSON literals are, luckily, a subset of KDL's literals. There are two ways to write a JSON literal into JiK:
|
||||
|
||||
* As a node with any nodename and a single argument, like `- true` (for the JSON `true`) or `foo 5` (for the JSON `5`).
|
||||
* When nested in arrays or objects, literals can usually be written as arguments (for array nodes) or properties (for object nodes). See below for details.
|
||||
|
||||
----
|
||||
|
||||
Literal nodes are used to represent a JSON literal, which luckily KDL's literal syntax is a superset of. They contain a single value, the literal they're representing. For example, to represent the JSON literal `true`, you'd write `- true` in JiK.
|
||||
JSON arrays are represented in JiK as a node with any nodename, with zero or more arguments and/or zero or more children with `-` nodenames.
|
||||
|
||||
(In many cases this isn't necessary, and KDL literals can be directly used instead. Literal nodes are necessary only for a top-level literal, or as item in an array.)
|
||||
Arguments can encode literals - for example, the JSON `[1, 2, 3]` can be written in JiK as `- 1 2 3`.
|
||||
|
||||
----
|
||||
|
||||
Array nodes are used to represent a JSON array. They can contain zero or more unnamed arguments, followed by zero or more child nodes; these are taken as the items of the array, in order of appearance.
|
||||
|
||||
This means that simple arrays of literals can be written compactly and simply; a JSON array like `[1,2,3]` can be written in JiK as `- 1 2 3`. When an array contains nested arrays or objects, the child nodes are used; a JSON array like `[1, [true, false], 3]` can be written in JiK as:
|
||||
Children can encode literals and/or nested arrays and objects. For example, the JSON `[1, [true, false], 3]` can be written in JiK as:
|
||||
|
||||
```kdl
|
||||
- {
|
||||
|
|
@ -29,7 +30,9 @@ This means that simple arrays of literals can be written compactly and simply; a
|
|||
}
|
||||
```
|
||||
|
||||
The two methods of writing children can be mixed, pulling the prefix of the array that is just literals into the arguments of the node. The preceding example could thus also be written as:
|
||||
The arguments and/or children, taken in order, represent the items of the array.
|
||||
|
||||
Arguments and children can be mixed, if desired. The preceding example could also be written as:
|
||||
|
||||
```kdl
|
||||
- 1 {
|
||||
|
|
@ -38,51 +41,72 @@ The two methods of writing children can be mixed, pulling the prefix of the arra
|
|||
}
|
||||
```
|
||||
|
||||
Two otherwise-ambiguous cases must be manually annotated with an `(array)` tag:
|
||||
|
||||
* A single-element array (such as `[1]`) written using arguments (as `- 1`) would be ambiguous with a literal node.
|
||||
To indicate this is an array, it must be written as `(array)- 1`
|
||||
(Or rewritten to use child nodes, like `- { - 1 }`.)
|
||||
* An empty array (JSON `[]`) must use the `(array)` tag, like `(array)-`.
|
||||
|
||||
The `(array)` tag can be used on any other valid array node if desired, but has no effect in such cases.
|
||||
|
||||
----
|
||||
|
||||
Object nodes are used to represent a JSON object. They can contain zero or more named properties, followed by zero or more child nodes; these are taken as the key/value pairs of the object, in order of appearance.
|
||||
JSON objects are represented in JiK as a node with any nodename, with zero or more properties and/or zero or more children with any nodenames.
|
||||
|
||||
If the value of a key/value pair is a literal, it can be encoded as a named property on the object. For example, the JSON object `{"foo": 1, "bar": true}` could be written in JiK as `- foo=1 bar=true`.
|
||||
Properties can encode literals - for example, the JSON `{"foo": 1, "bar": true}` can be written in JiK as `- foo=1 bar=true`.
|
||||
|
||||
Alternately, key/value pairs can be encoded as child nodes, using a type annotation on the node name to encode the key, and the node itself as the value. The preceding example could instead have been written as:
|
||||
Children can encode literals and/or nested arrays and objects,
|
||||
using the nodename for the item's key.
|
||||
For example, the JSON `{"foo": 1, "bar": [2, {"baz": 3}], "qux":4}` can be written in JiK as:
|
||||
|
||||
```kdl
|
||||
- {
|
||||
foo 1
|
||||
bar true
|
||||
}
|
||||
```
|
||||
|
||||
Of course, using children for literals is overly-verbose. It's only necessary when nesting arrays or objects into objects; for example, the JSON object `{"foo": [1, 2, {"bar": 3}], "baz":4}` can be written in JiK as:
|
||||
|
||||
```kdl
|
||||
- {
|
||||
foo 1 2 {
|
||||
- bar=3
|
||||
bar 2 {
|
||||
- baz=3
|
||||
}
|
||||
baz 4
|
||||
qux 4
|
||||
}
|
||||
```
|
||||
|
||||
As with arrays, child nodes and properties can be mixed. The precise order of a JSON object's keys isn't *meant* to be meaningful, so as long as that's true, *all* the keys with literal values can be pulled into the argument list. The preceding example could thus also be written as:
|
||||
As with arrays, child nodes and properties can be mixed, so the preceding example could have been written as:
|
||||
|
||||
```kdl
|
||||
- baz=4 {
|
||||
foo 1 2 {
|
||||
- bar=3
|
||||
- foo=1 {
|
||||
bar 2 {
|
||||
- baz=3
|
||||
}
|
||||
qux 4
|
||||
}
|
||||
```
|
||||
|
||||
Or, so long as the exact order of properties isn't meaningful (it's not *meant* to be in JSON),
|
||||
*all* the literal-valued keys can be pulled up into properties,
|
||||
leaving children nodes solely for nested arrays and objects:
|
||||
|
||||
```kdl
|
||||
- foo=1 qux=4 {
|
||||
bar 2 {
|
||||
- baz=3
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
----
|
||||
The properties and/or children of the node represent the items of the object,
|
||||
with the property names and child nodenames as each item's key.
|
||||
All "keys" in an object node must be unique.
|
||||
|
||||
There are two cases where there can be ambiguity between the three kinds of nodes. These can be solved by explicitly marking the node as an array or object using a tag.
|
||||
As with arrays, there are two ambiguous cases that must be manually annoted with the `(object)` tag:
|
||||
|
||||
An array with a single item cannot be represented using a node with a single value as that would make it a literal node. The `(array)` tag can be used to mark this node as an array instead.
|
||||
For example, the node `- true` is the literal `true`, while `(array)- true` is the array `[true]`.
|
||||
* An object containing a single item whose key is "-" (like `{"-": 1}`) written using children (like `- { - 1 }`)
|
||||
would be ambiguous with an array node.
|
||||
To indicate this is an object, it must be written as `(object)- { - 1 }`.
|
||||
(Or, if the sole item's value is a literal, as in this example,
|
||||
it can be rewritten to use properties, as `- -=1`.)
|
||||
* An empty object (JSON `{}`) must use the `(object)` tag, like `(object)-`.
|
||||
|
||||
An object with a single property named "-" that is encoded as a child node will be interpreted as an array with a single item. The `(object)` tag can be used to mark this node as an object instead.
|
||||
For example, `- { - true; }` is the array `[true]`, while `(object)- { - true; }` is the object `{"-": true}`.
|
||||
As with array nodes, `(object)` can be used on any valid object node if desired.
|
||||
|
||||
----
|
||||
|
||||
|
|
@ -92,15 +116,21 @@ Only valid JiK nodes can be encoded to JSON; if a JiK document contains an inval
|
|||
|
||||
* A literal node is valid if it contains a single unnamed argument.
|
||||
|
||||
* An array node is valid if it contains only unnamed arguments and/or child nodes named "-".
|
||||
* An array node is valid if it contains only unnamed arguments and/or child nodes named "-". If it contains no arguments and no child nodes, its nodename *must* have the `(array)` tag.
|
||||
|
||||
* An object node is valid if it contains only named properties and/or child nodes. Additionally, all "keys" must be unique within the node, whether they're encoded as property names or type annotations on node names.
|
||||
* An object node is valid if it contains only named properties and/or child nodes. Additionally, all "keys" must be unique within the node, whether they're encoded as property names or child node names. If it contains no properties and no child nodes, its nodename *must* have the `(object)` tag.
|
||||
|
||||
----
|
||||
|
||||
The name of the top-level JiK node is not taken into account. This allows for using a declarative node name instead of "-".
|
||||
Note that, outside of array/object items, the nodename is not meaningful in JiK.
|
||||
For simplicity, this document uses `-` for all such nodenames
|
||||
(and it is recommended that an automated JSON-to-KDL converter do the same),
|
||||
but this means it is possible to write a JiK object as meaningful KDL
|
||||
and embed it within a larger KDL document.
|
||||
|
||||
It is possible to embed JiK inside KDL documents. Here's a fictitious example describing an HTTP request with a JSON body, where the `body` node is an embedded JiK node.
|
||||
Here's a fictitious example describing an HTTP request with a JSON body,
|
||||
where the `body` node is an embedded JiK node
|
||||
that nevertheless reads as fairly natural KDL.
|
||||
|
||||
```kdl
|
||||
request "/api/cart" method="PUT" {
|
||||
|
|
@ -117,3 +147,14 @@ request "/api/cart" method="PUT" {
|
|||
}
|
||||
}
|
||||
```
|
||||
|
||||
The `body` node represents the JSON object
|
||||
|
||||
```json
|
||||
{
|
||||
"items": [
|
||||
{"id": 1234, "amount": 1},
|
||||
{"id": 2341, "amount": 2, "options": {"color": "red", "size": "XXL"}}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
|
|
|||
Loading…
Reference in New Issue