266 lines
8.8 KiB
Markdown
266 lines
8.8 KiB
Markdown
|
# zcl JSON Syntax Specification
|
||
|
|
||
|
This is the specification for the JSON serialization for zcl. zcl is a system
|
||
|
for defining configuration languages for applications. The zcl information
|
||
|
model is designed to support multiple concrete syntaxes for configuration,
|
||
|
and this JSON-based format complements [the native syntax](../zclsyntax/spec.md)
|
||
|
by being easy to machine-generate, whereas the native syntax is oriented
|
||
|
towards human authoring and maintenence.
|
||
|
|
||
|
This syntax is defined in terms of JSON as defined in
|
||
|
[RFC7159](https://tools.ietf.org/html/rfc7159). As such it inherits the JSON
|
||
|
grammar as-is, and merely defines a specific methodology for interpreting
|
||
|
JSON constructs into zcl structural elements and expressions.
|
||
|
|
||
|
This mapping is defined such that valid JSON-serialized zcl input can be
|
||
|
produced using standard JSON implementations in various programming languages.
|
||
|
_Parsing_ such JSON has some additional constraints not beyond what is normally
|
||
|
supported by JSON parsers, though adaptations are defined to allow processing
|
||
|
with an off-the-shelf JSON parser with certain caveats, described in later
|
||
|
sections.
|
||
|
|
||
|
## Structural Elements
|
||
|
|
||
|
The zcl language-agnostic information model defines a _body_ as an abstract
|
||
|
container for attribute definitions and child blocks. A body is represented
|
||
|
in JSON as a JSON _object_.
|
||
|
|
||
|
As defined in the language-agnostic model, body processing is done in terms
|
||
|
of a schema which provides context for interpreting the body's content. For
|
||
|
JSON bodies, the schema is crucial to allow differentiation of attribute
|
||
|
definitions and block definitions, both of which are represented via object
|
||
|
properties.
|
||
|
|
||
|
The special property name `"//"`, when used in an object representing a zcl
|
||
|
body, is parsed and ignored. A property with this name can be used to
|
||
|
include human-readable comments. (This special property name is _not_
|
||
|
processed in this way for any _other_ zcl constructs that are represented as
|
||
|
JSON objects.)
|
||
|
|
||
|
### Attributes
|
||
|
|
||
|
Where the given schema describes an attribute with a given name, the object
|
||
|
property with the matching name — if present — serves as the attribute's
|
||
|
definition.
|
||
|
|
||
|
When a body is being processed in the _dynamic attributes_ mode, each object
|
||
|
property serves as an attribute definition for the attribute whose name
|
||
|
matches the property name.
|
||
|
|
||
|
The value of an attribute definition property is interpreted as an _expression_,
|
||
|
as described in a later section.
|
||
|
|
||
|
Given a schema that calls for an attribute named "foo", a JSON object like
|
||
|
the following provides a definition for that attribute:
|
||
|
|
||
|
```json
|
||
|
{
|
||
|
"foo": "bar baz"
|
||
|
}
|
||
|
```
|
||
|
|
||
|
### Blocks
|
||
|
|
||
|
Where the given schema describes a block with a given type name, the object
|
||
|
property with the matching name — if present — serves as a definition of
|
||
|
zero or more blocks of that type.
|
||
|
|
||
|
Processing of child blocks is in terms of nested JSON objects and arrays.
|
||
|
If the schema defines one or more _labels_ for the block type, a nested
|
||
|
object is required for each labelling level, with the object keys serving as
|
||
|
the label values at that level.
|
||
|
|
||
|
After any labelling levels, the next nested value is either a JSON object
|
||
|
representing a single block body, or a JSON array of JSON objects that each
|
||
|
represent a single block body. Use of an array accommodates the definition
|
||
|
of multiple blocks that have identical type and labels.
|
||
|
|
||
|
Given a schema that calls for a block type named "foo" with no labels, the
|
||
|
following JSON objects are all valid definitions of zero or more blocks of this
|
||
|
type:
|
||
|
|
||
|
```json
|
||
|
{
|
||
|
"foo": {
|
||
|
"child_attr": "baz"
|
||
|
}
|
||
|
}
|
||
|
```
|
||
|
|
||
|
```json
|
||
|
{
|
||
|
"foo": [
|
||
|
{
|
||
|
"child_attr": "baz"
|
||
|
},
|
||
|
{
|
||
|
"child_attr": "boz"
|
||
|
}
|
||
|
]
|
||
|
}
|
||
|
```
|
||
|
```json
|
||
|
{
|
||
|
"foo": []
|
||
|
}
|
||
|
```
|
||
|
|
||
|
The first of these defines a single child block of type "foo". The second
|
||
|
defines _two_ such blocks. The final example shows a degenerate definition
|
||
|
of zero blocks, though generators should prefer to omit the property entirely
|
||
|
in this scenario.
|
||
|
|
||
|
Given a schema that calls for a block type named "foo" with _two_ labels, the
|
||
|
extra label levels must be represented as objects as in the following examples:
|
||
|
|
||
|
```json
|
||
|
{
|
||
|
"foo": {
|
||
|
"bar": {
|
||
|
"baz": {
|
||
|
"child_attr": "baz"
|
||
|
},
|
||
|
"boz": {
|
||
|
"child_attr": "baz"
|
||
|
}
|
||
|
},
|
||
|
"boz": {
|
||
|
"baz": {
|
||
|
"child_attr": "baz"
|
||
|
},
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
```
|
||
|
```json
|
||
|
{
|
||
|
"foo": {
|
||
|
"bar": {
|
||
|
"baz": {
|
||
|
"child_attr": "baz"
|
||
|
},
|
||
|
"boz": {
|
||
|
"child_attr": "baz"
|
||
|
}
|
||
|
},
|
||
|
"boz": {
|
||
|
"baz": [
|
||
|
{
|
||
|
"child_attr": "baz"
|
||
|
},
|
||
|
{
|
||
|
"child_attr": "boz"
|
||
|
}
|
||
|
]
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
```
|
||
|
|
||
|
Where multiple definitions are included for the same type and labels, the
|
||
|
JSON array is always the value of the property representing the final label,
|
||
|
and contains objects representing block bodies. It is not valid to use an array
|
||
|
at any other point in the block definition structure.
|
||
|
|
||
|
## Expressions
|
||
|
|
||
|
JSON lacks a native expression syntax, so the zcl JSON syntax instead defines
|
||
|
a mapping for each of the JSON value types, including a special mapping for
|
||
|
strings that allows optional use of arbitrary expressions.
|
||
|
|
||
|
### Objects
|
||
|
|
||
|
When interpreted as an expression, a JSON object represents a value of a zcl
|
||
|
object type.
|
||
|
|
||
|
Each property of the JSON object represents an attribute of the zcl object type.
|
||
|
The object type is constructed by enumerating the JSON object properties,
|
||
|
creating for each an attribute whose name exactly matches the property name,
|
||
|
and whose type is the result of recursively applying the expression mapping
|
||
|
rules.
|
||
|
|
||
|
An instance of the constructed object type is then created, whose values
|
||
|
are interpreted by again recursively applying the mapping rules defined in
|
||
|
this section.
|
||
|
|
||
|
It is an error to define the same property name multiple times within a single
|
||
|
JSON object interpreted as an expression.
|
||
|
|
||
|
### Arrays
|
||
|
|
||
|
When interpreted as an expression, a JSON array represents a value of a zcl
|
||
|
tuple type.
|
||
|
|
||
|
Each element of the JSON array represents an element of the zcl tuple type.
|
||
|
The tuple type is constructed by enumerationg the JSON array elements, creating
|
||
|
for each an element whose type is the result of recursively applying the
|
||
|
expression mapping rules. Correspondance is preserved between the array element
|
||
|
indices and the tuple element indices.
|
||
|
|
||
|
An instance of the constructed tuple type is then created, whose values are
|
||
|
interpreted by again recursively applying the mapping rules defined in this
|
||
|
section.
|
||
|
|
||
|
### Numbers
|
||
|
|
||
|
When interpreted as an expression, a JSON number represents a zcl number value.
|
||
|
|
||
|
zcl numbers are arbitrary-precision decimal values, so an ideal implementation
|
||
|
of this specification will translate exactly the value given to a number of
|
||
|
corresponding precision.
|
||
|
|
||
|
In practice, off-the-shelf JSON parsers often do not support customizing the
|
||
|
processing of numbers, and instead force processing as 32-bit or 64-bit
|
||
|
floating point values with a potential loss of precision. It is permissable
|
||
|
for a zcl JSON parser to pass on such limitations _if and only if_ the
|
||
|
available precision and other constraints are defined in its documentation.
|
||
|
Calling applications each have differing precision requirements, so calling
|
||
|
applications are free to select an implementation with more limited precision
|
||
|
capabilities should high precision not be required for that application.
|
||
|
|
||
|
### Boolean Values
|
||
|
|
||
|
The JSON boolean values `true` and `false`, when interpreted as expressions,
|
||
|
represent the corresponding zcl boolean values.
|
||
|
|
||
|
### The Null Value
|
||
|
|
||
|
The JSON value `null`, when interpreted as an expression, represents a
|
||
|
zcl null value of the dynamic pseudo-type.
|
||
|
|
||
|
### Strings
|
||
|
|
||
|
When intepreted as an expression, a JSON string may be interpreted in one of
|
||
|
two ways depending on the evaluation mode.
|
||
|
|
||
|
If evaluating in literal-only mode (as defined by the syntax-agnostic
|
||
|
information model) the literal string is intepreted directly as a zcl string
|
||
|
value, by directly using the exact sequence of unicode characters represented.
|
||
|
Template interpolations and directives MUST NOT be processed in this mode,
|
||
|
allowing any characters that appear as introduction sequences to pass through
|
||
|
literally:
|
||
|
|
||
|
```json
|
||
|
"Hello world! Template sequences like ${ are not intepreted here."
|
||
|
```
|
||
|
|
||
|
When evaluating in full expression mode (again, as defined by the syntax-
|
||
|
agnostic information model) the literal string is instead interpreted as a
|
||
|
_standalone template_ in the zcl Native Syntax. The expression evaluation
|
||
|
result is then the direct result of evaluating that template with the current
|
||
|
variable scope and function table.
|
||
|
|
||
|
```json
|
||
|
"Hello, ${name}! Template sequences are interpreted in full expression mode."
|
||
|
```
|
||
|
|
||
|
In particular the _Template Interpolation Unwrapping_ requirement from the
|
||
|
zcl native syntax specification must be implemented, allowing the use of
|
||
|
single-interpolation templates to represent expressions that would not
|
||
|
otherwise be representable in JSON, such as the following example where
|
||
|
the result must be a number, rather than a string representation of a number:
|
||
|
|
||
|
```json
|
||
|
"${ a + b }"
|
||
|
```
|