KSON Syntax & Grammar¶
A KSON document is a document containing exactly one KSON Value
KSON Values¶
KSON defines the following value types:
Nulls¶
A KSON document representing null is simply:
Booleans¶
Booleans are written true or false. Here is a KSON document representing a boolean value:
Numbers¶
Numbers in KSON are identical to numbers in JSON, with the exception that they may have leading zeros. Here is a KSON document representing a number:
Here is a KSON list demonstrating a variety of supported numbers:
Strings¶
Strings in KSON are identical to strings in JSON, except:
- KSON strings may be unquoted if they are 'simple', i.e. they only contain letters (from any alphabet), numbers or underscores, and they do not start with a number
- KSON strings may be quoted using a single quote rather than a double quote. Here is a list of strings demonstrating the difference:
- 'A "string", enclosed in single quotes'
- "A 'string', enclosed in double quotes"
- 'use slash to escape \'internal\' quotes'
- "use slash to escape \"internal\" quotes"
- KSON strings may contain raw, unescaped whitespace (though an Embed Block will usually be a better choice for multiline strings due to their editing ergonomics and indent-stripping)
Lists¶
A list in Kson is a sequence of Kson Values.
KSON supports three list styles:
- Plain Dash Lists, a clean YAML-like list style useful for clarity and readability
- Delimited Dash Lists, a delimited version of Plain Dash Lists useful when editing or refactoring complex nested lists
- Bracket Lists a JSON-like list style useful for compacting lists and required for JSON compatibility
Plain Dash Lists¶
A Plain Dash List is a clean YAML-like un-delimited list useful for clarity and readability. This is the style used in Kson's plain format.
A plain dash list is un-delimited, denotes each list element with a -.
A empty list is expressed as: <>
Plain dash lists may be nested like so:
Important: unlike YAML, whitespace is not significant in KSON, so to add more outer elements to the above list, we
must explicitly end the nested list with =, the end-dash:
The end-dash =¶
The end-dash = explicitly declares the end of a Plain Dash List. The end-dash is only required
when a nested Plain Dash List needs to explicitly denote the end of the items that belong to it:
- outer_element_1
-
- nested_element_1
- nested_element_2
=
# note that without the end-dash, `outer_element_2` would belong to the nested list
- outer_element_2
The end-dot . works analogously for Plain Objects.
Delimited Dash Lists¶
A Delimited Dash List is a delimited version of a Plain Dash List, useful when editing or refactoring complex nested lists—for instance, these lists can be safely and unambiguously copy/pasted around.
Delimited Dash Lists are delimited with angle brackets <...>:
An empty list is expressed as: <>
This style is used in Kson's delimited format
Bracket Lists¶
A Bracket List is a JSON-like list useful for supporting compact formatted lists and required for JSON compatibility.
Bracket Lists are delimited with square brackets [...]:
Note
Commas are supported between elements, but they are unnecessary and generally formatted away. See Commas in Kson for detail
Objects¶
Object properties in KSON are key: value pairs where key is a String and value is
any Kson Value.
KSON supports two object styles:
- Plain Objects, a clean YAML-like un-delimited object style useful for clarity and readability
- Delimited Objects, a JSON-like delimited version of Plain Objects useful when editing or refactoring complex nested objects—for instance, these objects can be safely and unambiguously copy/pasted around
Plain Objects¶
A plain object in KSON is a clean YAML-like un-delimited object style useful for clarity and readability.
An empty object is expressed as: {}
Plain objects may be nested like so:
Important: unlike YAML, whitespace is not significant in KSON, so to add more outer properties to the above object,
we must explicitly end the nested object with ., the end-dot:
outer_property:
nested_property_1: 1
nested_property_2: 2
.
outer_property_2: 'not part of the nested object'
The end-dot .¶
The end-dot . explicitly declares the end of a Plain Object. The end-dot is only required when
a Plain Object needs to explicitly denote the end of the properties that belong to it, i.e.:
outer_property_1:
nested_property_1: x
nested_element_2: y
.
# note that without the end-dot, `outer_element_2` would belong to the nested object
outer_element_2: 'a value'
The end-dash = works analogously for Plain Dash Lists.
Delimited Objects¶
A Delimited Object is a a JSON-like delimited version of Plain Objects useful when editing or refactoring complex nested objects.
Delimited Objects are delimited with curly braces {...}:
An empty object is expressed as: {}
This style is used in Kson's delimited format
Note
Commas are supported between properties, but they are unnecessary and generally formatted away. See Commas in Kson for detail
Embed Blocks¶
The KSON Embed Block is designed for ergonomically embedding complex content such as code
blocks. These blocks start on the first newline after the opening %, run all the way up
to the closing %% and always strip their minimum indent:
Embed blocks often feature an Embed Tag denoting the type of text being embedded. This is particularly useful for embedded code so that editors and tooling can key off this value and provide language-specific features for a given embed block
%typescript
function fibonacci(n: number): number {
if (n <= 0) return 0;
if (n === 1) return 1;
return fibonacci(n - 1) + fibonacci(n - 2);
}
%%
See Embed Preamble for full details on the metadata supported by Embed Blocks
Escaping Embed Delimiters¶
The rules for escaping embed block delimiters are as follows:
embed_escapes: %
Embed end-delimiters are escaped by putting a slash inside them: %\%
Note that this moves the escaping goalpost since we also need to allow "%\%"
literally inside of embeds. So: when evaluating escaped embed delimiters,
we allow arbitrary `\`s between `%`s, and consume one of them. Thus, %\\%
gives %\% in the output, %\\\% gives %\\% in the output, etc forever until
we hit an uninterrupted end-delimiter:
%%
Alternative Embed Block Delimiter¶
The alternative embed delimiter $ works identically to % and can be used when convenient to minimize
escaping
alternate_embed: $kson
This embed block is equivalent to a %/%%-delimited block, but here "$/$"
must be escaped rather than "%%"
$$
The Embed Preamble¶
The Embed Preamble may be provided to annotate the embedded content. An Embed Preamble is made of two parts:
- the Embed Tag, which denotes the content type
- the Embed Metadata, arbitrary metadata given after the first colon
:in the Embed Preamble
An empty Embed Preamble simply indicates an embed of raw text. When appropriate, it's recommended to include a tag or metadata to provide context about the embedded content.
Embed Tags¶
Embed Blocks can denote the type of content being embedded using an Embed Tag. Embed Tags appear at the beginning of the Embed Preamble and may not contain newlines or colons.
%sql
SELECT first_name, last_name, hire_date
FROM employees
WHERE department = 'Sales'
AND hire_date < '2020-01-01'
ORDER BY hire_date ASC;
%%
While KSON does not specify which tags are legal, KSON does specify that Embed Tags are intended to denote the type of embedded content, and they intended to enable tooling enhancements for the embedded content—for instance to properly highlight the embedded content, or to automatically inject a full-on embedded editor. See Embed Metadata for syntax to arbitrarily embellish this tag.
Embed Metadata¶
Embed Blocks may have a string of arbitrary metadata associated with them. Embed Metadata is given after the first colon in the Embed Preamble:
%:Captain's log, Stardate 4523.3
Deep Space Station K-7 has issued a priority one call. More than an emergency,
it signals near or total disaster. We can only assume the Klingons have attacked
the station. We're going in armed for battle.
%%
Embed Metadata is often used in conjunction with an Embed Tag:
%sql: "server=10.0.1.174;uid=root;database=company"
SELECT first_name, last_name, hire_date
FROM employees
WHERE department = 'Sales'
AND hire_date < '2020-01-01'
ORDER BY hire_date ASC;
%%
Formatting Styles¶
KSON has a built-in auto-formatter that supports three opinionated styles. These three "views" on the same data serve different roles:
- Plain format, a lean format reminiscent of YAML. This is KSON's default format, useful for clarity and readability
- Delimited format, an explicit format reminiscent of JSON, useful when editing or refactoring complex nested data
- Compact format, a minified format to facilitate compression and transport
Plain Format Example¶
person:
name: 'Leonardo Bonacci'
nickname: Fibonacci
favorite_books:
- title: Elements
author: Euclid
- title: Metaphysics
author: Aristotle
.
favorite_numbers:
-
- 0
- 1
- 1
- 2
- '...'
=
- '(1 + √5)/2'
- π
# A Kson "embed block" containing Kotlin code
favorite_function: %kotlin
/**
* Calculates the nth number in the Fibonacci sequence using recursion
*/
fun fibonacci(n:
Int): Long {
if (n < 0) throw IllegalArgumentException("Input must be non-negative")
return when (n) {
0 -> 0
1 -> 1
else -> fibonacci(n - 1) + fibonacci(n - 2)
}
}
%%
Delimited Format Example¶
{
person: {
name: 'Leonardo Bonacci'
nickname: Fibonacci
favorite_books: <
- {
title: Elements
author: Euclid
}
- {
title: Metaphysics
author: Aristotle
}
>
favorite_numbers: <
- <
- 0
- 1
- 1
- 2
- '...'
>
- '(1 + √5)/2'
- π
>
# A Kson "embed block" containing Kotlin code
favorite_function: %kotlin
/**
* Calculates the nth number in the Fibonacci sequence using recursion
*/
fun fibonacci(n:
Int): Long {
if (n < 0) throw IllegalArgumentException("Input must be non-negative")
return when (n) {
0 -> 0
1 -> 1
else -> fibonacci(n - 1) + fibonacci(n - 2)
}
}
%%
}
}
Compact Format Example¶
person:name:'Leonardo Bonacci'nickname:Fibonacci favorite_books:[{title:Elements author:Euclid}title:Metaphysics author:Aristotle.]favorite_numbers:[[0 1 1 2 '...']'(1 + √5)/2' π]
# A Kson "embed block" containing Kotlin code
favorite_function:%kotlin
/**
* Calculates the nth number in the Fibonacci sequence using recursion
*/
fun fibonacci(n:
Int): Long {
if (n < 0) throw IllegalArgumentException("Input must be non-negative")
return when (n) {
0 -> 0
1 -> 1
else -> fibonacci(n - 1) + fibonacci(n - 2)
}
}
%%
JSON Compatibility Notes¶
KSON is a superset of JSON¶
KSON's status as a superset of JSON is verified by JsonSuiteTest.kt, which is generated from JSONTestSuite
JSON Schema support¶
Kson is compatible with JsonSchema (Draft7, more to come), verified by SchemaSuiteTest.kt, generated from JSON-Schema-Test-Suite
Commas in Kson¶
JSON compatibility requires that Objects and Bracket Lists in KSON allow commas. However, since every KSON value has a definite beginning and ending, the commas are unnecessary and generally formatted away.