Android Components > Libraries > JEXL
Javascript Expression Language: Powerful context-based expression parser and evaluator.
This implementation is based on Mozjexl, a fork of Jexl (designed and created at TechnologyAdvice) for use at Mozilla, specifically as a part of SHIELD and Normandy.
Features not supported yet:
- JavaScript object properties (e.g. String.length)
- Adding custom operators (binary/unary)
Other implementations:
Use Gradle to download the library from maven.mozilla.org (Setup repository):
implementation "org.mozilla.components:jexl:{latest-version}
val jexl = Jexl()
val result = jexl.evaluate("75 > 42")
// evaluate() returns an object of type JexlValue. Calling toKotlin() converts this
// into a matching Kotlin type (in this case a Boolean).
println(result.value) // Prints "true"
Often expressions should return a Boolean
value. In this case evaluateBooleanExpression
is a helper that always returns a Kotlin Boolean
and never throws an exception (Returns false).
val jexl = Jexl()
// "result" has type Boolean and value "true"
val result = jexl.evaluateBooleanExpression("42 + 23 > 50", defaultValue = false)
Operation | Symbol |
---|---|
Negate | ! |
Operation | Symbol |
---|---|
Add, Concat | + |
Subtract | - |
Multiply | * |
Divide | / |
Divide and floor | // |
Modulus | % |
Power of | ^ |
Logical AND | && |
Logical OR | || |
Comparison | Symbol |
---|---|
Equal | == |
Not equal | != |
Greater than | > |
Greater than or equal | >= |
Less than | < |
Less than or equal | <= |
Element in array or string | in |
Conditional expressions check to see if the first segment evaluates to a truthy value. If so, the consequent segment is evaluated. Otherwise, the alternate is. If the consequent section is missing, the test result itself will be used instead.
Expression | Result |
---|---|
"" ? "Full" : "Empty" |
Empty |
"foo" in "foobar" ? "Yes" : "No" |
Yes |
{agent: "Archer"}.agent ?: "Kane" |
Archer |
Type | Examples |
---|---|
Booleans | true , false |
Strings | "Hello "user"", 'Hey there!' |
Integers | 6, -7, 5, -3 |
Doubles | -7.2, -3.14159 |
Objects | {hello: "world!"} |
Arrays | ['hello', 'world!'] |
Undefined | undefined |
The JavaScript implementation of Jexl uses a Numeric
type. This implementation dynamically casts between Integer
and Double
as needed.
Parentheses work just how you'd expect them to:
Expression | Result |
---|---|
(83 + 1) / 2 |
42 |
1 < 3 && (4 > 2 || 2 > 4) |
true |
Access variables in the context object by just typing their name. Objects can be traversed with dot notation, or by using brackets to traverse to a dynamic property name.
Example context:
{
name: {
first: "Malory",
last: "Archer"
},
exes: [
"Nikolai Jakov",
"Len Trexler",
"Burt Reynolds"
],
lastEx: 2
}
Expression | Result |
---|---|
name.first |
Malory |
name['la' + 'st'] |
Archer |
exes[2] |
Burt Reynolds |
exes[lastEx - 1] |
Len Trexler |
Collections, or arrays of objects, can be filtered by including a filter expression in brackets. Properties of each collection can be referenced by prefixing them with a leading dot. The result will be an array of the objects for which the filter expression resulted in a truthy value.
Example context:
{
employees: [
{first: 'Sterling', last: 'Archer', age: 36},
{first: 'Malory', last: 'Archer', age: 75},
{first: 'Lana', last: 'Kane', age: 33},
{first: 'Cyril', last: 'Figgis', age: 45},
{first: 'Cheryl', last: 'Tunt', age: 28}
],
retireAge: 62
}
Expression | Result |
---|---|
employees[.first == 'Sterling'] |
[{first: 'Sterling', last: 'Archer', age: 36}] |
employees[.last == 'Tu' + 'nt'].first |
Cheryl |
employees[.age >= 30 && .age < 40] |
[{first: 'Sterling', last: 'Archer', age: 36},{first: 'Lana', last: 'Kane', age: 33}] |
employees[.age >= 30 && .age < 40][.age < 35] |
[{first: 'Lana', last: 'Kane', age: 33}] |
employees[.age >= retireAge].first |
Malory |
The power of Jexl is in transforming data. Transform functions take one or more arguments: The value to be transformed, followed by anything else passed to it in the expression.
val jexl = Jexl()
jexl.addTransform("split") { value, arguments ->
value.toString().split(arguments.first().toString()).toJexlArray()
}
jexl.addTransform("lower") { value, _ ->
value.toString().toLowerCase().toJexl()
}
jexl.addTransform("last") { value, _ ->
(value as JexlArray).values.last()
}
Expression | Result |
---|---|
`"Pam Poovey"|lower|split(' ') | first` |
"password==guest"|split('=' + '=') |
['password', 'guest'] |
Variable contexts are straightforward Objects that can be accessed in the expression.
val context = Context(
"employees" to JexlArray(
JexlObject(
"first" to "Sterling".toJexl(),
"last" to "Archer".toJexl(),
"age" to 36.toJexl()),
JexlObject(
"first" to "Malory".toJexl(),
"last" to "Archer".toJexl(),
"age" to 75.toJexl()),
JexlObject(
"first" to "Malory".toJexl(),
"last" to "Archer".toJexl(),
"age" to 33.toJexl())
)
)
Expression | Result |
---|---|
employees[.age >= 30 && .age < 40] |
[{first=Sterling, last=Archer, age=36}, {first=Malory, last=Archer, age=33}] |
employees[.age >= 30 && .age < 90][.age < 37] |
[{first=Malory, last=Archer, age=33}] |
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/