-
Notifications
You must be signed in to change notification settings - Fork 538
JSONM
JSONM is a JSON extension that allows using macro definitions inside JSON files.
The main goals of this format are to make configuration files more readable and to get rid of scripts that generate huge configuration files.
JSONM is superset of JSON. Any JSON object may be treated as JSON with macros. JSON with macros is also a valid JSON object – the difference lies in enhancing some JSON properties to allow reuse of similar parts of large JSON objects.
Using a simple preprocessor, we generate standard JSON objects from user-friendly JSONM by processing all macros and substituting all constants. .
JSONM is a JSON object with a special optional key macros
:
{
"macros": {
"macroName1": macroDefinition,
"macroName2": macroDefinition,
…
},
"customProperty1": valueWithMacros,
"customProperty2": valueWithMacros
}
-
value
any JSON value (string, object, number, etc.) -
valueWithMacros
JSON value that may containmacroCall
,paramSubstitution
,builtInCall
-
macroCall
"@macroName(param1,param2,…)"
or
{
"type": "macroName",
"paramName1": valueWithMacros,
"paramName2": valueWithMacros,
…
}
-
paramSubstitution
string of form %paramName%. Examples: "%paramName%", "a%paramName%b". %paramName% will be replaced with value of the corresponding parameter. If the whole string is a parameter substitution (i.e., "%paramName%") parameter values may be any valueWithParams. Otherwise, it should be a string. -
builtInCall
Basically the same as template call, but has no short form.
{
"type": "if|transform|process",
… params for this call …
}
JSONM allows C-style comments, which are removed by the preprocessor. Example:
{
// some comment here
"key": /* and one more comment here */ "value/**/"
}
After preprocessing:
{
"key": "value/**/"
}
Macro is a reusable piece of JSON. One can think about it as a function that
takes an arbitrary list of values and returns valueWithMacros
.
The "macros" property should be an object with macro definitions. Syntax is following:
{
"macros": {
"macroName": {
"type": "macroDef",
"params": macroDefParamList,
"result": valueWithMacros
}
}
}
"result" is JSONM that may contain paramSubstitution
.
-
macroDefParamList
[ macroDefParam, macroDefParam, …]
-
macroDefParam
"paramName" | { "name": "paramName", "default": value }
Example:
{
"pair": {
"type": "macroDef",
"params": [ "key", "value" ],
"result": {
"%key%": "%value%"
}
},
"fullName": {
"type": "macroDef",
"params": [ "first", "last" ],
"result": [ "@pair(first,%first%)", "@pair(last,%last%)" ]
}
}
Parameters may have defaults. Example:
{
"car": {
"type": "macroDef",
"params": [
"model",
// parameter with default
{
"name": "color",
"default": "green"
}
],
"result": {
"model": "%model%",
"color": "%color%"
}
}
}
Given an object with the macros from our previous examples, other properties may include macro calls:
{
"person": "@fullName(John, Doe)",
"car": "@car(Mercedes)"
}
Trailing and leading spaces are trimmed from arguments. After preprocessing:
{
"person": {
"first": "John",
"last": "Doe"
},
"car": {
"model": "Mercedes",
"color": "green"
}
}
Consts are valueWithMacros
that may be substituted everywhere. One may use
only built-in macros and built-in calls in consts.
{
"macros": {
"author": {
"type": "constDef",
"result": "John Doe"
},
"copyright": {
"type": "constDef",
"result": "%author% owns it"
}
},
"file": "%copyright%. Some content"
}
After preprocessing:
{
"file": "John Doe owns it. Some content"
}
These characters have special meaning for the preprocessor: ‘@', ‘%', ‘(', ‘)', ‘,'. Add two backslashes (\) before any character to escape the character. It will then be added to the string 'as is' and will not be interpreted as a preprocessor instruction. To escape a backslash, write \\. Two backslashes are required because JSON uses a single backslash () as an escape character. Example:
{
"email": "fake\\@fake.fake",
"valid": "100\\%",
"backslash": "\\\\"
}
After preprocessing:
{
"email": "fake@fake.fake",
"valid": "100%",
"backslash": "\\"
}
Note: "backslash" is JSON property, so it will be interpreted as only one backslash.
These macros perform different operations on JSON values.
Usage: @import(path)
Allows loading JSONM from external source. Example:
File: cities.json
{
"cities": [
"New York",
"Washington"
]
}
File: city.json
{
"city": {
"type": "select",
"key": 0,
"dictionary": "@import(cities.json) "
}
}
After preprocessing, city.json
becomes:
{
"city": "New York"
}
Usage: @int(5)
; @str(@int(5))
; @bool(true)
@int
casts its argument to an integer:
{
"key": "@int(100)"
}
After preprocessing:
{
"key": 100
}
@str
casts its argument to string; @bool
to boolean.
Usage: @keys(object)
; @values(object)
@keys
returns list of object keys; @values
returns list of object values:
{
"type": "keys",
"dictionary": {"a": 1, "b": 2}
}
After preprocessing:
["a", "b"]
Usage:
"type": "merge",
"params": [ list1, list2, list3, ... ]
or
"type": "merge",
"params": [ obj1, obj2, obj3, ... ]
or
"type": "merge"
"params": [ str1, str2, str3, ... ]
Combines multiple strings, lists or objects into one.
In case params
is a list of strings, merge
concatenates them.
{
"type": "merge",
"params": [
[1, 2],
[3, 4]
]
}
After preprocessing:
[1, 2, 3, 4]
If params
is a list of objects, it will also be merged :
{
"type": "merge",
"params": [
{"a": 1, "b": 2},
{"b": 3, "c": 4}
]
}
After preprocessing:
{
"a": 1,
"b": 3,
"c": 4
}
Note: properties of obj{N}
will override properties of obj{N-1}
.
Usage: @select(obj,string)
or @select(list,int)
Returns element from list or object.
{
"type": "select",
"key": "a",
"dictionary": {
"a": 1,
"b": 2
}
}
After preprocessing:
1
Usage: @shuffle(list)
Randomly shuffles a list.
{
"type": "shuffle",
"dictionary": [1, 2, 3, 4]
}
After preprocessing, (one possible example):
[2, 4, 3, 1]
Usage:
"type": "slice",
"dictionary": obj,
"from": string,
"to": string
or
"type": "slice",
"dictionary": list/string,
"from": int,
"to": int
Returns a slice (subrange) of list, object or string:
- in case of list range of elements
from <= id <= to
. - in case of object range of properties with keys
from <= key <= to
. - in case of string substring
[from, to]
Note: from and to are inclusive
{
"type": "slice",
"from": 1,
"to": 2,
"dictionary": [1, 2, 3, 4]
}
After preprocessing:
[2, 3]
Usage:
"type": "size",
"dictionary": list/string/object
Returns size of object/array/string.
{
"type": "size",
"dictionary": [1, 2],
}
After preprocessing:
2
Usage:
"type": "sort",
"dictionary": list,
Sort a list of strings/numbers.
{
"type": "sort",
"dictionary": [2, 1],
}
After preprocessing:
[1, 2]
Usage: @range(@int(1),@int(2))
Returns list of integers [from, from + 1, ..., to]
{
"myRange": "@range(@int(1),@int(2))"
}
After preprocessing:
{
"myRange": [1, 2]
}
Usage: @isArray(value)
; isBool(value)
; etc.
Return true if value is list, bool, int, object or string respectively:
"@isString(abc)"
After preprocessing:
true
Usage: @add(A,B)
; @sub(A,B)
; etc.
Perform corresponding operation on integers:
// 2*3 + 5
{ "value": "@add(@mul(@int(2),@int(3)),@int(5))" }
After preprocessing:
"value": 11
Usage: @contains(dictionary,value)
Returns true if dictionary contains a key; list contains a value; string contains a substring:
{ "condition": "@contains(abacaba,aca)" }
After preprocessing:
{ "condition": true }
Usage: @empty(dictionary)
Returns true if object, array or string is empty.
Usage:
"type": "split"
"dictionary": string,
"delim": string
Splits input string by delimiter and returns a list of pieces.
{
"type": "split",
"dictionary": "a.b.c.",
"delim": "."
}
After preprocessing:
[ "a", "b", "c", "" ]
Usage:
"type": "set"
"dictionary": array or object,
"key": string or int,
"value": any value
For array, returns input array dictionary
with item at index key
set to value
. 0 <= key
<= @size(%dictionary%)
For object, returns input object dictionary
with property key
set to value
.
Usage: @defined(name)
Returns true
if the name is defined in local context or in consts (i.e. check if macro, variable, parameter or constant with the given name exists.
Returns true if A is less than B. Can compare any values except objects.
{ "condition": "@less(bcd,abcd)" }
After preprocessing:
{ "condition": false }
Usage: @equals(A,B)
Returns true if A == B
. Can compare any values.
Returns true if A and B
; A or B
respectively. Both A and B should be booleans.
Usage: @not(A)
Returns true if not A
.
Usage:
"type": "if",
"condition": bool,
"is_true": any value
"is_false": any value
Conditional operator: returns is_true
property if condition
is true, is_false
otherwise:
{
"value": {
"type": "if",
"condition": "@equals(a,a)"
"is_false": "Oops",
"is_true": "Yeah"
}
}
After preprocessing:
{ "value": "Yeah" }
Usage:
"type": "transform",
"dictionary": obj,
"itemTransform": macro with extended context
"keyTranform": macro with extended context (optional)
"itemName": string (optional, default: item)
"keyName": string (optional, default: key)
or
"type": "transform",
"dictionary": list,
"itemTranform": macro with extended context
"keyName": string (optional, default: key)
"itemName": string (optional, default: item)
Transforms elements of a list or object, using itemTransform
and keyTransform
properties. keyTransform
is optional; available only if the dictionary is an
object. itemTransform
and keyTransform
are valueWithMacros
and may use
two additional parameters: key
and item
(parameter names are configured
with keyName
and itemName
properties).
{
"type": "transform",
"keyTransform": "%item%",
"itemTransform": "%key%",
"dictionary": {
"a": "b",
"b": "c"
}
}
After preprocessing:
{
"b": "a",
"c": "b"
}
Usage:
"type": "process",
"initialValue": any value,
"transform": macro with extended context
"keyName": string (optional, default: key)
"itemName": string (optional, default: item)
"valueName": string (optional, default: value)
Iterates over an object or array and transforms "value". Literally:
value = initialValue
for each (key, item) in dictionary:
value = transform(key, item, value)
}
return value
transform
is valueWithMacros
and may use three additional
parameters: key
, item
and value
(parameter names are configured
with keyName
, itemName
and valueName
properties).
{
"type": "process",
"initialValue": "",
"dictionary": ["a", "b", "c", "d"],
"transform": "%item%%value%"
}
After preprocessing:
"dcba"
Usage:
"type": "foreach",
"key": string (optional, default: key)
"item": string (optional, default: item)
"from": object or list
"where": macro with extended context (optional, %key% and %item%)
"use": macro with extended context (optional, %key% and %item%)
"top": int (optional)
"noMatchResult": any value (optional)
foreach (key, item) from where use top for top items from dictionary "from" which satisfy "where" condition merge expansions into one dictionary.
For example, to filter dictionary:
{
"type": "foreach",
"from": <dictionary>,
"where": <condition>
}
To convert object to list:
{
"type": "foreach",
"from": <object>,
"use": [ <list item> ]
"noMatchResult": []
}
To grab at most 2 items from that satisfy :
{
"type": "foreach",
"from": <dictionary>
"where": <condition>
"top": 2
}
- Installation
- Common setups
- Concepts
- Features
- Configuration
- Monitoring
- Error Handling
- Announcements