Skip to content
Branch: master
Find file History
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
..
Failed to load latest commit information.
src
README.md
build.gradle
proguard-rules.pro

README.md

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:

Usage

Setting up the dependency

Use Gradle to download the library from maven.mozilla.org (Setup repository):

implementation "org.mozilla.components:jexl:{latest-version}

Evaluating expressions

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 Booleanvalue. 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)

Unary Operators

Operation Symbol
Negate !

Binary Operators

Operation Symbol
Add, Concat +
Subtract -
Multiply *
Divide /
Divide and floor //
Modulus %
Power of ^
Logical AND &&
Logical OR ||

Comparison

Comparison Symbol
Equal ==
Not equal !=
Greater than >
Greater than or equal >=
Less than <
Less than or equal <=
Element in array or string in

Ternary operator

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

Native Types

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.

Groups

Parentheses work just how you'd expect them to:

Expression Result
(83 + 1) / 2 42
1 < 3 && (4 > 2 &#124;&#124; 2 > 4) true

Identifiers

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

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

Transforms

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"&#124;split('=' + '=') ['password', 'guest']

Context

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}]

License

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/
You can’t perform that action at this time.