Skip to main content

Hosted extractor mapping language

The hosted extractor mapping language translates JSON structures to other JSON structures. The language is similar to JavaScript and is purely functional. A mapping is a single expression that describes how to build one JSON structure from another.

Variables

Use these input objects to define a mapping:

  • input - This is the content of the received message, parsed into an object.

  • context - This contains information about the message. The content of this object varies between the different hosted extractors.

    For MQTT, the context object contains

    • topic: which topic the message arrived at

For examples of using these variables, see Handling more data in a single message.

When variables are objects or arrays they can be selected from. For example, given the object

{
"someField": 123,
"someArray": [1, 2, 3, { "nested": [1, 2, 3] }]
}

you can select from an object with

input.someField -> 123

or

input["someField"] -> 123

You select from arrays with

input.someArray[0] -> 1

Selectors can be nested as

input.someArray[3].nested[2] -> 3

Selectors can contain expressions

input.someArray[1 + 1] -> 3

Functions

The hosted extractor mapping language contains a set of predefined functions, which can be used to transform the input in other ways.

For example,

float("1.4") -> 1.4

will try to transform the input from a string to a number, which may fail.

Any function can also be called postfix, as a method on a value, so you could instead write

"1.4".float() -> 1.4

This is especially useful when dealing with chained operations.

See also Functions.

Lambda expressions

Some functions can take user defined functions as arguments, such as map, reduce, or filter. To create the functions for these, use Lambda notation: argument => return.

If the function takes no arguments, use () as the input to the function. If a function takes multiple values, denote them as a tuple: (arg1, arg2).

Examples

If we want to turn the list [1, 2, 3, 4] into [2, 4, 6, 8] we can use map to apply a function to each member of the list, and write a lambda function that doubles each value:

[1, 2, 3, 4].map(number => number * 2) -> [2, 4, 6, 8]

If you want to remove all nulls from a list, use the filter function, and write a lambda function that returns true if the value is the null type using the is operator:

[0, 1, 2, 3, 4, null, 5].filter(item => !(item is "null")) -> [0, 1, 2, 3, 4, 5]

To sum all the numbers from 1 through 10, use the reduce function to iteratively add the next member of the list to an aggregate:

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10].reduce((sum, next) => sum + next, 0) -> 110

Operators

The operators take one or two inputs and output some value. The operators are:

Plus

1 + 2 + 3 -> 6

Adds two numbers together.

Minus

1 - 2 - 3 -> -4

Subtracts a number from another.

Multiply

2 * 3 -> 6

Multiplies two numbers.

Divide

3 / 2 -> 1.5

Divides one number by another.

And

true && false -> false

Boolean AND, returns true if the inputs are both not null or false.

Or

true || false -> true

Boolean OR, returns true if either input is not null or false.

Equals

1 == 2 -> false

Checks for exact equality. The inputs must be the same type, or this will be false.

Not equals

1 != 2 -> true

Checks for inequality.

Greater than

2 > 3 -> false

Checks if the first number is greater than the second number.

Greater than or equal

2 >= 3 -> false

Checks if the first number is greater than or equal to the second number.

Less than

2 < 3 -> true

Checks if the first number is less than the second number.

Less than

2 <= 3 -> true

Checks if the first number is less than or equal to the second number.

Modulo

5 % 2 -> 1

Computes the modulus, or remainder, of the first argument divided by the second argument.

Is

5 is float

This is the type check operator, used to check if the first argument is the type given by the second argument. The valid types are

  • null
  • object
  • array
  • string
  • number
  • float
  • int
  • bool

Any float or int is also number.

If expressions

You can use functional-style if expressions. For example

if 2 > 3 {
"hello"
} else if 2 == 2 {
"world"
} else {
"goodbye"
}

will return "world". There is an if function as well, but using expressions usually results in cleaner code.

Objects

You can create JSON objects with familiar JSON syntax:

{
"hello": 123,
"test": input.something
}

These objects can be accessed, just like input variables:

{ "hello": 123 }.hello

You can concat objects using the ... operator:

{
"test": 123,
...{
"hello": "world"
},
...{
"world": "hello"
}
}

will yield the object

{
"test": 123,
"hello": "world",
"world": "hello"
}

Object keys can also be expressions:

{ concat("he", "llo"): "world" }

will yield an object

{ "hello": "world" }

Arrays

Arrays can be created using a JSON syntax:

[1, 2, 3]

You can access arrays using brackets, just like with inputs:

[1, 2, 3][0] -> 1

Arrays also support the ... operator:

[
1,
...[2, 3],
...[4, 5]
]

will yield

[1, 2, 3, 4, 5]