VM based implementation of JSLT - which is a JSON query and transformation language.
Given input:
{
"name" : "Brett Favre",
"team" : "Green Bay Packers"
}
and given the JSLT script:
{
"player" : .name + " played for " + .team
}
outputs:
{
"player" : "Brett Favre played for Green Bay Packers"
}
Include in your Maven/Gradle project:
<!-- https://mvnrepository.com/artifact/com.github.tonysparks.jslt2/jslt2 -->
<dependency>
<groupId>com.github.tonysparks.jslt2</groupId>
<artifactId>jslt2</artifactId>
<version>0.2.8</version>
</dependency>
How to initialize and evaluate a template expression:
// use out of the box defaults
Jslt2 runtime = new Jslt2();
// or, customize the runtime...
Jslt2 runtime = Jslt2.builder()
.resourceResolver(ResourceResolvers.newFilePathResolver(new File("./examples")))
.enableDebugMode(true)
.objectMapper(new ObjectMapper())
.maxStackSize(1024 * 5)
.build();
// Execute a template:
JsonNode input = ...; // some input Json
JsonNode result = runtime.eval("{ \"input\" : . }", input);
// or, compile a template for repeated use:
Template template = runtime.compile("{ \"input\" : . }");
JsonNode result1 = template.eval(input);
JsonNode input2 = ...; // different json input
JsonNode result2 = template.eval(input2);
Jslt2 runtime = Jslt2.builder().includeNulls(true).build();
/*
and */
syntax let x = """this
is a "verbatim"
string"""
for
expressions. The variable is injected with the value of the current iteration index (zero based) and stored in the variable index__
. This variable exists for both Object and Array for
expressions:{for ([1,2,3])
("key_" + $index__) : .
}
// outputs:
{
"key_0":1,
"key_1":2,
"key_2":3
}
If you need to embed for
expressions, you can reference the parent index by:
{for ([1,2,3])
let parentIndex = $index__
("key_" + $index__) : [for (["a", "b", "c"]) $parentIndex * $index__ ]
}
// outputs:
{
"key_0":[0,0,0],
"key_1":[0,1,2],
"key_2":[0,2,4]
}
async
blocks allow for running expressions in separate threads. This can improve performance for long running expressions.
NOTE: This is currently an experimental feature. As an example:
// runs the makeSlowDatabaseQuery functions in background threads, which allows them to be computed
// in parallel
async {
let a = makeSlowDatabaseQuery(.someParam)
let b = makeSlowDatabaseQuery(.someOtherParam)
} // evaluation will block here until all let expressions have been computed
{
"a": $a, // we can now reference the computed value of $a in our template expression
"b": $b,
}
There are several limitations or "gotchas" with async
blocks:
async {
let a = "foo"
let b = "bar" + $a // INVALID, can't reference $a as this value will be computed in parallel with $b
}
let y = "bar"
async {
let a = $x // INVALID because x is defined AFTER the async block
let b = $y // valid, because y is defined BEFORE the async block
}
let x = "foo"
async {
let a = "hi"
}
let x = $a // can reference $a in a new variable
def y() $a // can reference $a in a function
{
"result": $a // can reference $a in the template expression
}
You can customize the ExecutorService
provided to the Jslt2
runtime. The default ExecutorService
uses daemon threads and Executors.newCachedThreadPool
.
// Customize (or use an already created instance) of ExecutorService
ExecutorService service = ...
Jslt2 runtime = Jslt2.builder()
.executorService(service)
.build();