Skip to content

Guide

Based on a running example (1), the language elements are introduced according to their intended use. The json example describes an flat owner with his personal data and the flat inventory. The inventory consists of items purchased on a specific day.

  1. running example
    {
      "flatOwner": {
        "name": "Miller",
        "age": 28,
        "location": "Berlin, Germany",
        "#post-code": 12205,
        "mobility": {
          "driverLic": true,
          "car": "transporter"
        },
        "favorites": [ "Coen", "Dylan", "Nietzsche" ]
      },
      "inventory": [
        {
          "date": "2023-10-05",
          "items": [
            {
              "category": "furniture",
              "name": "chair",
              "price": 50.00,
              "quantity": 3
            }
          ]
        },
        {
          "date": "2024-10-01",
          "items": [
            {
              "category": "electronic",
              "name": "pc",
              "price": 1800.78,
              "quantity": 1
            },
            {
              "category": "furniture",
              "name": "table",
              "price": 100.10,
              "quantity": 1
            }
          ]
        }
      ]
    }
    

Selection & Restriction

flatOwner.(name + `#post-code`) 

(1)

  1. running example
    {
      "flatOwner": {
        "name": "Miller",
        "age": 28,
        "location": "Berlin, Germany",
        "#post-code": 12205,
        "mobility": {
          "driverLic": true,
          "car": "transporter"
        },
        "favorites": [ "Coen", "Dylan", "Nietzsche" ]
      },
      "inventory": [
        {
          "date": "2023-10-05",
          "items": [
            {
              "category": "furniture",
              "name": "chair",
              "price": 50.00,
              "quantity": 3
            }
          ]
        },
        {
          "date": "2024-10-01",
          "items": [
            {
              "category": "electronic",
              "name": "pc",
              "price": 1800.78,
              "quantity": 1
            },
            {
              "category": "furniture",
              "name": "table",
              "price": 100.10,
              "quantity": 1
            }
          ]
        }
      ]
    }
    

selects the name and the (non-identifier) property for the postal code with the result "Miller12205" .1

flatOwner./name|#.*/ 

(1)

  1. running example
    {
      "flatOwner": {
        "name": "Miller",
        "age": 28,
        "location": "Berlin, Germany",
        "#post-code": 12205,
        "mobility": {
          "driverLic": true,
          "car": "transporter"
        },
        "favorites": [ "Coen", "Dylan", "Nietzsche" ]
      },
      "inventory": [
        {
          "date": "2023-10-05",
          "items": [
            {
              "category": "furniture",
              "name": "chair",
              "price": 50.00,
              "quantity": 3
            }
          ]
        },
        {
          "date": "2024-10-01",
          "items": [
            {
              "category": "electronic",
              "name": "pc",
              "price": 1800.78,
              "quantity": 1
            },
            {
              "category": "furniture",
              "name": "table",
              "price": 100.10,
              "quantity": 1
            }
          ]
        }
      ]
    }
    

selects all properties with names matching a regular expression: "Miller" , 12205 . 2

flatOwner.favorites.*   and   flatOwner.mobility.* 

(1)

  1. running example
    {
      "flatOwner": {
        "name": "Miller",
        "age": 28,
        "location": "Berlin, Germany",
        "#post-code": 12205,
        "mobility": {
          "driverLic": true,
          "car": "transporter"
        },
        "favorites": [ "Coen", "Dylan", "Nietzsche" ]
      },
      "inventory": [
        {
          "date": "2023-10-05",
          "items": [
            {
              "category": "furniture",
              "name": "chair",
              "price": 50.00,
              "quantity": 3
            }
          ]
        },
        {
          "date": "2024-10-01",
          "items": [
            {
              "category": "electronic",
              "name": "pc",
              "price": 1800.78,
              "quantity": 1
            },
            {
              "category": "furniture",
              "name": "table",
              "price": 100.10,
              "quantity": 1
            }
          ]
        }
      ]
    }
    

selects all array items and property values and results in "Coen" , "Dylan" , "Nietzsche"  and true , "transporter" , respectively.

inventory.* ?(date < '2024-10-04').
    items.* ?(category == 'furniture').
        (price * quantity)

(1)

  1. running example
    {
      "flatOwner": {
        "name": "Miller",
        "age": 28,
        "location": "Berlin, Germany",
        "#post-code": 12205,
        "mobility": {
          "driverLic": true,
          "car": "transporter"
        },
        "favorites": [ "Coen", "Dylan", "Nietzsche" ]
      },
      "inventory": [
        {
          "date": "2023-10-05",
          "items": [
            {
              "category": "furniture",
              "name": "chair",
              "price": 50.00,
              "quantity": 3
            }
          ]
        },
        {
          "date": "2024-10-01",
          "items": [
            {
              "category": "electronic",
              "name": "pc",
              "price": 1800.78,
              "quantity": 1
            },
            {
              "category": "furniture",
              "name": "table",
              "price": 100.10,
              "quantity": 1
            }
          ]
        }
      ]
    }
    

selects inventory items restricted to dates and an item category and calculates their total prices 150 , 100.1 .

inventory.*.
  (
    d = date,
    // '_' is the context node passed by 'inventory.*'
    _ ?(date.match('\\d{4}-10-\\d{2}')).
      items.*.
        (quantity + ' ' + name + ': '
         + (price * quantity) + '€ '
         + '(' + $d + ')'
        )
  )

(1)

  1. running example
    {
      "flatOwner": {
        "name": "Miller",
        "age": 28,
        "location": "Berlin, Germany",
        "#post-code": 12205,
        "mobility": {
          "driverLic": true,
          "car": "transporter"
        },
        "favorites": [ "Coen", "Dylan", "Nietzsche" ]
      },
      "inventory": [
        {
          "date": "2023-10-05",
          "items": [
            {
              "category": "furniture",
              "name": "chair",
              "price": 50.00,
              "quantity": 3
            }
          ]
        },
        {
          "date": "2024-10-01",
          "items": [
            {
              "category": "electronic",
              "name": "pc",
              "price": 1800.78,
              "quantity": 1
            },
            {
              "category": "furniture",
              "name": "table",
              "price": 100.10,
              "quantity": 1
            }
          ]
        }
      ]
    }
    

selects all items purchased in any October and calculates their total prices The output contains the date inherited from the inventory property. For this purpose, the inventory date is assigned to a variable (d = date) and used later ($d) to produce the output

"3 chair: 150€ (2023-10-05)" , "1 pc: 1800.78€ (2024-10-01)" , "1 table: 100.1€ (2024-10-01)" 

The following example make use of the object returned by the match function.

flatOwner.location.match('(.*), (.*)').
  ('city: ' + groups[1] + ', country: ' + groups[2])

(1)

  1. running example
    {
      "flatOwner": {
        "name": "Miller",
        "age": 28,
        "location": "Berlin, Germany",
        "#post-code": 12205,
        "mobility": {
          "driverLic": true,
          "car": "transporter"
        },
        "favorites": [ "Coen", "Dylan", "Nietzsche" ]
      },
      "inventory": [
        {
          "date": "2023-10-05",
          "items": [
            {
              "category": "furniture",
              "name": "chair",
              "price": 50.00,
              "quantity": 3
            }
          ]
        },
        {
          "date": "2024-10-01",
          "items": [
            {
              "category": "electronic",
              "name": "pc",
              "price": 1800.78,
              "quantity": 1
            },
            {
              "category": "furniture",
              "name": "table",
              "price": 100.10,
              "quantity": 1
            }
          ]
        }
      ]
    }
    

The first line yields the object {"groups": ["Berlin, Germany","Berlin","Germany"]} that contains the regex groups from which the result "city: Berlin, country: Germany"  is built.

Construction & Transformation

A construction is a superset of json, allowing, for example, unquoted property names. If the context node is used - on top level equivalent to the input - we can speak of a transformation. For instance, the following expression

{
  driverLic: flatOwner.mobility.driverLic,
  car: "van"
}

(1)

  1. running example
    {
      "flatOwner": {
        "name": "Miller",
        "age": 28,
        "location": "Berlin, Germany",
        "#post-code": 12205,
        "mobility": {
          "driverLic": true,
          "car": "transporter"
        },
        "favorites": [ "Coen", "Dylan", "Nietzsche" ]
      },
      "inventory": [
        {
          "date": "2023-10-05",
          "items": [
            {
              "category": "furniture",
              "name": "chair",
              "price": 50.00,
              "quantity": 3
            }
          ]
        },
        {
          "date": "2024-10-01",
          "items": [
            {
              "category": "electronic",
              "name": "pc",
              "price": 1800.78,
              "quantity": 1
            },
            {
              "category": "furniture",
              "name": "table",
              "price": 100.10,
              "quantity": 1
            }
          ]
        }
      ]
    }
    

yields { "driverLic": true, "car": "van" }  as the result.

inventory.*.
  (
    d = date,
    _ ?($d.match('\\d{4}-10-\\d{2}')).
      items.*.
        {
          date: $d,
          // only non-id properties must be quoted
          'total price': price*quantity
        }
  )

(1)

  1. running example
    {
      "flatOwner": {
        "name": "Miller",
        "age": 28,
        "location": "Berlin, Germany",
        "#post-code": 12205,
        "mobility": {
          "driverLic": true,
          "car": "transporter"
        },
        "favorites": [ "Coen", "Dylan", "Nietzsche" ]
      },
      "inventory": [
        {
          "date": "2023-10-05",
          "items": [
            {
              "category": "furniture",
              "name": "chair",
              "price": 50.00,
              "quantity": 3
            }
          ]
        },
        {
          "date": "2024-10-01",
          "items": [
            {
              "category": "electronic",
              "name": "pc",
              "price": 1800.78,
              "quantity": 1
            },
            {
              "category": "furniture",
              "name": "table",
              "price": 100.10,
              "quantity": 1
            }
          ]
        }
      ]
    }
    

constructs json objects as the result instead of strings (see previous example):

{ "date": "2023-10-05", "total price": 150 } , { "date": "2024-10-01", "total price": 1800.78 } , { "date": "2024-10-01", "total price": 100.1 } 

In some applications it can happen that property names are not fixed and contain information that is otherwise given as a value. Furthermore, transformations are based on embedding input objects into the output:

flatOwner.
  {
    prop(name): {
      _,
      name: none,
      location: location.match('.*, (.*)').groups[1],
      mobility: none,
      favorites: none
    }
  }

(1)

  1. running example
    {
      "flatOwner": {
        "name": "Miller",
        "age": 28,
        "location": "Berlin, Germany",
        "#post-code": 12205,
        "mobility": {
          "driverLic": true,
          "car": "transporter"
        },
        "favorites": [ "Coen", "Dylan", "Nietzsche" ]
      },
      "inventory": [
        {
          "date": "2023-10-05",
          "items": [
            {
              "category": "furniture",
              "name": "chair",
              "price": 50.00,
              "quantity": 3
            }
          ]
        },
        {
          "date": "2024-10-01",
          "items": [
            {
              "category": "electronic",
              "name": "pc",
              "price": 1800.78,
              "quantity": 1
            },
            {
              "category": "furniture",
              "name": "table",
              "price": 100.10,
              "quantity": 1
            }
          ]
        }
      ]
    }
    

transforms the running example into an object that has the top property name stemming from a property value, using the expression prop(name), where name is the flat owner property containing its name. Furthermore, the flatOwner is embedded (_), the location modified, and some properties are removed (via keyword none):

{ "Miller": { "age": 28, "location": "Germany", "#post-code": 12205 } } 

The expression

{
  flatOwner.mobility,
  inventory[0].items[0]
}

(1)

  1. running example
    {
      "flatOwner": {
        "name": "Miller",
        "age": 28,
        "location": "Berlin, Germany",
        "#post-code": 12205,
        "mobility": {
          "driverLic": true,
          "car": "transporter"
        },
        "favorites": [ "Coen", "Dylan", "Nietzsche" ]
      },
      "inventory": [
        {
          "date": "2023-10-05",
          "items": [
            {
              "category": "furniture",
              "name": "chair",
              "price": 50.00,
              "quantity": 3
            }
          ]
        },
        {
          "date": "2024-10-01",
          "items": [
            {
              "category": "electronic",
              "name": "pc",
              "price": 1800.78,
              "quantity": 1
            },
            {
              "category": "furniture",
              "name": "table",
              "price": 100.10,
              "quantity": 1
            }
          ]
        }
      ]
    }
    

performs the classical merge of multiple objects and has output

{ "driverLic": true, "car": "transporter", "category": "furniture", "name": "chair", "price": 50, "quantity": 3 } 

A conditional embedding

flatOwner.mobility.{ if (driverLic) _ } 

(1)

  1. running example
    {
      "flatOwner": {
        "name": "Miller",
        "age": 28,
        "location": "Berlin, Germany",
        "#post-code": 12205,
        "mobility": {
          "driverLic": true,
          "car": "transporter"
        },
        "favorites": [ "Coen", "Dylan", "Nietzsche" ]
      },
      "inventory": [
        {
          "date": "2023-10-05",
          "items": [
            {
              "category": "furniture",
              "name": "chair",
              "price": 50.00,
              "quantity": 3
            }
          ]
        },
        {
          "date": "2024-10-01",
          "items": [
            {
              "category": "electronic",
              "name": "pc",
              "price": 1800.78,
              "quantity": 1
            },
            {
              "category": "furniture",
              "name": "table",
              "price": 100.10,
              "quantity": 1
            }
          ]
        }
      ]
    }
    

has output { "driverLic": true, "car": "transporter" } .

Conditionals plays an important role, not only in the form of filters, introduced earlier. The following example places a condition within a filter. Furthermore, the embedding of sequences in arrays is used. This is also called list comprehension in other functional languages.

heavyCar = (flatOwner.mobility.car == 'transporter'),
{
  transport: [
    inventory.*.items.*
      ?(
        q = quantity,
        if ($heavyCar)
            (category == 'furniture' and $q > 1)
            (category != 'furniture')
      )
  ]
}

(1)

  1. running example
    {
      "flatOwner": {
        "name": "Miller",
        "age": 28,
        "location": "Berlin, Germany",
        "#post-code": 12205,
        "mobility": {
          "driverLic": true,
          "car": "transporter"
        },
        "favorites": [ "Coen", "Dylan", "Nietzsche" ]
      },
      "inventory": [
        {
          "date": "2023-10-05",
          "items": [
            {
              "category": "furniture",
              "name": "chair",
              "price": 50.00,
              "quantity": 3
            }
          ]
        },
        {
          "date": "2024-10-01",
          "items": [
            {
              "category": "electronic",
              "name": "pc",
              "price": 1800.78,
              "quantity": 1
            },
            {
              "category": "furniture",
              "name": "table",
              "price": 100.10,
              "quantity": 1
            }
          ]
        }
      ]
    }
    

stores the car in a variable for later use, and then builds the array by iterating over the inventory items and filtering them according to the quantity and the car. The filter is dynamically evaluated via the variable heavyCar. It yields output

{ "transport": [ { "category": "furniture", "name": "chair", "price": 50, "quantity": 3 } ] } 

Last, but not least, variables can also store sequences. In the following, two sequences are assigned to variables. Afterwards, a 'join' is performed on the names of the elements, and again embedded in an array.

preferred = ['chair', 'pc'].*,
items = inventory.*.items.*,
[
  $preferred.(
      p=_,
      $items ?(name == $p))
]

(1)

  1. running example
    {
      "flatOwner": {
        "name": "Miller",
        "age": 28,
        "location": "Berlin, Germany",
        "#post-code": 12205,
        "mobility": {
          "driverLic": true,
          "car": "transporter"
        },
        "favorites": [ "Coen", "Dylan", "Nietzsche" ]
      },
      "inventory": [
        {
          "date": "2023-10-05",
          "items": [
            {
              "category": "furniture",
              "name": "chair",
              "price": 50.00,
              "quantity": 3
            }
          ]
        },
        {
          "date": "2024-10-01",
          "items": [
            {
              "category": "electronic",
              "name": "pc",
              "price": 1800.78,
              "quantity": 1
            },
            {
              "category": "furniture",
              "name": "table",
              "price": 100.10,
              "quantity": 1
            }
          ]
        }
      ]
    }
    
[ { "category": "furniture", "name": "chair", "price": 50, "quantity": 3 }, { "category": "electronic", "name": "pc", "price": 1800.78, "quantity": 1 } ] 

Step Functions

The following simple step function selectByDate implements the filter from a previous example. Its context node is that of the call and its parameters are referenced with $, like variables.

func selectByDate(date, dateRegex) = _ ?( $date.match( $dateRegex ) ),

inventory.*.
  (
    d = date,
    selectByDate($d, '\\d{4}-10-\\d{2}').
      items.*.
        {
          date: $d,
          // only non-id properties must be quoted
          'total price': price*quantity
        }
  )

(1)

  1. running example
    {
      "flatOwner": {
        "name": "Miller",
        "age": 28,
        "location": "Berlin, Germany",
        "#post-code": 12205,
        "mobility": {
          "driverLic": true,
          "car": "transporter"
        },
        "favorites": [ "Coen", "Dylan", "Nietzsche" ]
      },
      "inventory": [
        {
          "date": "2023-10-05",
          "items": [
            {
              "category": "furniture",
              "name": "chair",
              "price": 50.00,
              "quantity": 3
            }
          ]
        },
        {
          "date": "2024-10-01",
          "items": [
            {
              "category": "electronic",
              "name": "pc",
              "price": 1800.78,
              "quantity": 1
            },
            {
              "category": "furniture",
              "name": "table",
              "price": 100.10,
              "quantity": 1
            }
          ]
        }
      ]
    }
    

Constructions can also be reused with the help of functions. The example given below modifies the previous one by outsourcing the construction part into the function itemTotal.

func selectByDate(date, dateRegex) = _ ?( $date.match( $dateRegex ) ),

func itemTotal(d, total) = (
    {
        date: $d,
        'total price': $total
    }
),

inventory.*.(
    d = date,
    selectByDate($d, '\\d{4}-10-\\d{2}').
        items.*.itemTotal($d, price*quantity)
)

(1)

  1. running example
    {
      "flatOwner": {
        "name": "Miller",
        "age": 28,
        "location": "Berlin, Germany",
        "#post-code": 12205,
        "mobility": {
          "driverLic": true,
          "car": "transporter"
        },
        "favorites": [ "Coen", "Dylan", "Nietzsche" ]
      },
      "inventory": [
        {
          "date": "2023-10-05",
          "items": [
            {
              "category": "furniture",
              "name": "chair",
              "price": 50.00,
              "quantity": 3
            }
          ]
        },
        {
          "date": "2024-10-01",
          "items": [
            {
              "category": "electronic",
              "name": "pc",
              "price": 1800.78,
              "quantity": 1
            },
            {
              "category": "furniture",
              "name": "table",
              "price": 100.10,
              "quantity": 1
            }
          ]
        }
      ]
    }
    

Note that in this particular case, the function body of itemTotal would not have to be enclosed in parentheses. However, this is advisable for complex functions.

Javascript

Step functions in the host language (e.g., javascript) are used to realize functionality that is not available in apath itself. They have to follow a certain form, which is described in section Step Functions).

function formatCurrency(ctx_node, locales, currency) {
  if (typeof ctx_node !== 'number') return undefined
  return new Intl.NumberFormat(locales, { style: 'currency', currency })
      .format(ctx_node)
}

For instance, the previous function formats the context node, that have to be a number, according to locales and currency.

inventory.*.
  (
    _ ?(date.match('\\d{4}-10-\\d{2}')).
      items.*.
        (quantity + ' ' + name + ': '
         // step func call for formatting
         + (price * quantity).formatCurrency('de-DE', 'EUR')
        )
  )

(1)

  1. running example
    {
      "flatOwner": {
        "name": "Miller",
        "age": 28,
        "location": "Berlin, Germany",
        "#post-code": 12205,
        "mobility": {
          "driverLic": true,
          "car": "transporter"
        },
        "favorites": [ "Coen", "Dylan", "Nietzsche" ]
      },
      "inventory": [
        {
          "date": "2023-10-05",
          "items": [
            {
              "category": "furniture",
              "name": "chair",
              "price": 50.00,
              "quantity": 3
            }
          ]
        },
        {
          "date": "2024-10-01",
          "items": [
            {
              "category": "electronic",
              "name": "pc",
              "price": 1800.78,
              "quantity": 1
            },
            {
              "category": "furniture",
              "name": "table",
              "price": 100.10,
              "quantity": 1
            }
          ]
        }
      ]
    }
    

The expression above makes use of it for the prices and yields the following result.

"3 chair: 150,00 €" , "1 pc: 1.800,78 €" , "1 table: 100,10 €" 

The parameters and return values of javascript Step functions can vary depending on the application (see cheat-sheet for more examples).


  1. here '+' is the concatenation of strings. ↩

  2. Multiple results are delimited by comma in the example output. ↩