- Josson is a complete query and transformation language for JSON.
- Jossons is a template engine to generate text output.
https://mvnrepository.com/artifact/com.octomix.josson/josson
<dependency>
<groupId>com.octomix.josson</groupId>
<artifactId>josson</artifactId>
<version>1.4.5</version>
</dependency>
implementation group: 'com.octomix.josson', name: 'josson', version: '1.4.5'
- Query a JSON dataset.
- There are 245 internal functions to manipulate and format data.
- Restructure JSON data and capable of grouping and unwind data.
- Can be used as an API parameter to trim down the response JSON result.
- Is a template engine to fill in placeholders and generate text output.
- Support XML and HTML escaping.
- Resolve template placeholder by querying data from multiple Josson objects.
- Resolve template placeholder from external data source on demand.
- Join two JSON datasets to build a new dataset.
- Set operation on two datasets.
- I used Jossons to generate millions of SMS/Email notifications during the first year.
- I used Jossons to generate plain text and csv reports that retrieve data from MongoDB directly.
- I store the notification or report definitions in template documents. No need to write extra program coding for different template.
Operator | Description |
---|---|
( | Grouping |
) | Grouping |
= | Is equal to (support object and array) |
!= | Not equal to (support object and array) |
> | Greater than |
>= | Greater than or equal to |
< | Less than |
<= | Less than or equal to |
=~ | Left matches regular expression |
!=~ | Not match regular expression |
! | Logical NOT |
& | Logical AND |
| | Logical OR |
Operator | Description |
---|---|
>=< | Inner join |
<=< | Left join one |
>=> | Right join one |
<=<< | Left join many |
>>=> | Right join many |
<!< | Left excluding join |
>!> | Right excluding join |
<!> | Outer excluding join |
<+< | Left concatenate |
>+> | Right concatenate |
<-< | Subtract right from left |
>-> | Subtract left from right |
<-> | Symmetric difference |
<u> | Union |
>n< | Intersection |
| | Chaining pipe |
<==> | Equals (returns BooleanNode) |
<!=> | Not equals (returns BooleanNode) |
Initial setup for date time formatting and JSON serialization.
Josson.setLocale(Locale.ENGLISH); // default Locale.getDefault()
Josson.setZoneId(ZoneId.of("Asia/Hong_Kong")); // default ZoneId.systemDefault()
Josson.setSerializationInclusion(JsonInclude.Include.NON_NULL);
To create a Josson object from a Jackson JsonNode.
Josson josson = Josson.create(jsonNode);
To create a Josson object from a Java object.
Josson josson = Josson.from(object);
To create a Josson object from a JSON string.
Josson josson = Josson.fromJsonString("...");
To apply a Josson query path and get the result JsonNode.
JsonNode node = josson.getNode(expression);
Define initial variables for a query. Variable name must start with "$".
Map<String, JsonNode> vars = new HashMap<>();
vars.put("$a", IntNode.valueOf(3));
JsonNode node = josson.getNode("concat('qty=',$a)", param);
To apply a Josson query path and get the path trace along the main branch. The trace object contains all progressive nodes and variables defined along the main branch.
PathTrace trace = josson.getPathTrace(expression);
A Josson Path is constructed with Path Steps connected by .
.
A path step can...
- Return an element of an object node by key name.
- Filter an array node, return the first matching element or all matching elements.
- Perform a transformation operation by a Josson Function.
Step Syntax | Description |
---|---|
key |
A child element key name |
[number] |
An array element by zero-based index |
[expression] |
A boolean filter expression to find the first matching array element |
[expression]* |
A boolean filter expression to query all matching array elements |
[expression]@ |
Filter all matching elements and divert each to separate branches |
[]@ |
Divert each element of the current array node to separate branches |
array@ |
Divert each array element to separate branches |
function() |
A Josson function |
@function() |
Merge all branch results into a single array before manipulation |
function()@ |
Divert the function output array elements to separate branches |
* |
Wildcard search that return the first resolvable non-null result |
** |
Wildcard search that return all object elements |
*@ |
Wildcard search and divert each object element to separate branches |
*(expression) |
Multi-level wildcard search and return the first resolvable element |
*[expression] |
Wildcard search with filter and return the first matching element |
*[expression]* |
Wildcard search with filter and return all matching elements |
*[expression]@ |
Wildcard search with filter and divert each element to separate branches |
~'regex' |
Search by regular expression and return the first matching element |
~'regex'* |
Search by regular expression and return all matching elements |
~'regex'@ |
Search by regular expression and divert each element to separate branches |
To specify an array and then apply an index or a filter can be simplified by removing the .
between them.
The following two statements produce the same final result. But have different number of path steps.
The first one contains two path steps, select an array and then filter.
The second one contains one path step only, filter an array directly.
array.[expression]*
array[expression]*
Filter can also apply to an object node.
If the expression is evaluated to true
, the object itself is returned.
Otherwise, return null
.
Example
{
"a": {
"b": {
"c": [1, 2, "d"]
},
"x": {
"y": "z"
}
}
}
a.b.c ==> [1, 2, "d"]
a.b.c[0] ==> 1
a.b.c[2].upperCase() ==> "D"
a.b.c[isNumber()]* ==> [ 1, 2 ]
a.x[y='z'] ==> { "y": "z" }
a.x[y='1'] ==> null
a.*.y ==> "z"
Enclose the key name with double quote if it contains "." or starts/ends with spaces.
Example
{
"a.b": {
" c.d ": 123
}
}
"a.b"." c.d " ==> 123
Wildcard search and regular expression search path steps work on object node.
Multi-level wildcard searches for the first resolvable result on path steps in order.
No argument or an argument of value 0
means unlimited levels.
Example
// Search for *.y
*(1).y ==> null
// Search for *.y and then *.*.y
*(2).y ==> "z"
// Unlimited levels
*(0).y ==> "z"
*().y ==> "z"
A wildcard search with filter is actually a combination of 3 steps. The following query is the same as a wildcard search with filter that returns all matching elements:
Josson function entries()
> Find-all array filter [expression]*
> Select element value
// *[expression]* is expanded to...
entries().[expression]*.value
Function entries()
transform object { "name" : <JsonNode>, ... }
into this new structure:
[
{
"key" : "name",
"value" : <JsonNode>
},
:
:
]
Therefore, use keyword key
in a wildcard filter expression to search.
Even keyword value
can also be used in wildcard filter expression.
Example
*[key.startsWith('item') & value.isText()]
*[key.matches('^[A-Z]{10}$')]*
*[key =~ '^[A-Z]{10}$']*
entries().[key =~ '^[A-Z]{10}$']*.value
The last 3 examples do the same thing and can be simplified to this syntax:
~'^[A-Z]{10}$'*
Additional step symbols are available in filter expression and function argument.
Step | Operation | Description |
---|---|---|
$ |
N/A | Restart from the root node |
.. |
N/A | Node of the previous step (each additional dot go back one more step) |
... |
N/A | Node of the previous step's previous step |
$letVar |
N/A | Variable defined by function let() |
? |
Aggregate | Current array node |
? |
Scalar | Current non-array node or current array node's each element |
@ |
Scalar | Current array node |
# |
Scalar | Zero-based index of an array element |
## |
Scalar | One-based index of an array element |
#A |
Scalar | Uppercase alphabetic index of an array element |
#a |
Scalar | Lowercase alphabetic index of an array element |
#R |
Scalar | Uppercase roman numerals index of an array element |
#r |
Scalar | Lowercase roman numerals index of an array element |
Exception
Function let()
is not counted as a path step.
Josson path chart shows data type changes and data flow along the path. Data filtering, transformation and formatting details are not included.
Element | Description |
---|---|
→ |
A progress step |
⇒ |
An end result |
"" |
A text node |
$I |
An integer node |
$D |
A double node |
$TF |
A boolean node |
{} |
An object node |
{=} |
An object validator |
[] |
An array node |
[]@ |
Divert each array element to separate branches |
[#] |
An indexed array element |
[=] |
A find-first filter |
[=]* |
A find-all filter |
[=]@ |
A find-all filter and divert each element to separate branches |
/ @ \ |
Divert to separate branches |
\ @ → [] / |
Merge branches into an array |
func() |
A Josson function |
(%) |
Function argument, the current object's child node |
(?) |
Scalar function argument, the current non-array node itself or array node's each element |
(?[]) |
Aggregate function argument, the current array node |
(@) |
Scalar function argument, the current array node |
!! |
The position where the step is unresolvable |
Below is the JSON for this tutorial.
{
"salesOrderId": "SO0001",
"salesDate": "2022-01-01T10:01:23",
"salesPerson": "Raymond",
"customer": {
"customerId": "CU0001",
"name": "Peggy",
"phone": "+852 62000610"
},
"items": [
{
"itemCode": "B00001",
"name": "WinWin TShirt Series A - 2022",
"brand": "WinWin",
"property": {
"size": "M",
"colors": [ "WHITE", "RED" ]
},
"qty": 2,
"unit": "Pcs",
"unitPrice": 15.0,
"tags": [ "SHIRT", "WOMEN" ]
},
{
"itemCode": "A00308",
"name": "OctoPlus Tennis Racket - Star",
"brand": "OctoPlus",
"property": {
"colors": [ "BLACK" ]
},
"qty": 1,
"unit": "Pcs",
"unitPrice": 150.0,
"unitDiscount": 10.0,
"tags": [ "TENNIS", "SPORT", "RACKET" ]
},
{
"itemCode": "A00201",
"name": "WinWin Sport Shoe - Super",
"brand": "WinWin",
"property": {
"size": "35",
"colors": [ "RED" ]
},
"qty": 1,
"unit": "Pair",
"unitPrice": 110.0,
"unitDiscount": 10.0,
"tags": [ "SHOE", "SPORT", "WOMEN" ]
}
],
"totalAmount": 270.0
}
-
To query a value node.
josson.getNode("salesPerson") ==> "Raymond"
Path chart
{} → salesPerson ⇒ ""
-
Node name is case-sensitive. Josson returns null value if the path is unresolvable.
josson.getNode("salesperson") ==> !unresolvable!
Path chart
{} → salesperson!! ⇒ !unresolvable!
-
To query an object node.
josson.getNode("customer") ==> { "customerId" : "CU0001", "name" : "Peggy", "phone" : "+852 62000610" }
Path chart
{} → customer{} ⇒ {}
-
Parent node and child node are connected by a
.
.josson.getNode("customer.name") ==> "Peggy"
Path chart
{} → customer{} → name ⇒ ""
-
Function is constructed by a function name followed by parentheses with optional comma-separated arguments.
A function manipulate the current node and produce an output along the path.josson.getNode("customer.name.upperCase()") ==> "PEGGY"
Path chart
{} → customer{} → name → upperCase(?) ⇒ ""
-
Function name is case-insensitive.
If one more parameter is given in addition to a function's maximum number of argument, the function will manipulate the data that evaluated from the 1st parameter. This mechanism does not apply to function that accept unlimited number of arguments.
e.g. upperCase() needs 0 argument. If 1 parameter is given, upperCase() will manipulate that data.josson.getNode("customer.UPPERCase(name)") ==> "PEGGY"
Path chart
{} → customer{} → upperCase(%) ⇒ ""
-
If the function is the first path step, it works on the root node.
josson.getNode("upperCase(customer.name)") ==> "PEGGY"
Path chart
{} → upperCase(%) ⇒ ""
-
Functions can be nested and the parameters can refer to those child nodes of the same step.
josson.getNode("customer.concat(upperCase(name), ' / ', phone)") ==> "PEGGY / +852 62000610"
Path chart
{} → customer{} → concat(%) ⇒ ""
-
A path start with numbers override the data and produces an integer node.
josson.getNode("123") ==> 123
Path chart
$I ⇒ $I
-
A path start with numbers and has
.
produces a double node.josson.getNode("123.40") ==> 123.4
Path chart
$D ⇒ $D
-
A path start and end with single quote
'
override the data and produces a text string node.
If the string literal contains a single quote, it is replaced by two single quotes.josson.getNode("'She said, ''Go ahead''.'") ==> "She said, 'Go ahead'."
Path chart
"" ⇒ ""
-
A path start with
true
orfalse
override the data and produces a boolean node.josson.getNode("true.not()") ==> false
Path chart
$TF → not(?) ⇒ $TF
-
To query an array node.
josson.getNode("items") ==> [ { "itemCode" : "B00001", "name" : "WinWin TShirt Series A - 2022", "brand" : "WinWin", "property" : { "size" : "M", "colors" : [ "WHITE", "RED" ] }, "qty" : 2, "unit" : "Pcs", "unitPrice" : 15.0, "tags" : [ "SHIRT", "WOMEN" ] }, { "itemCode" : "A00308", "name" : "OctoPlus Tennis Racket - Star", "brand" : "OctoPlus", "property" : { "colors" : [ "BLACK" ] }, "qty" : 1, "unit" : "Pcs", "unitPrice" : 150.0, "unitDiscount" : 10.0, "tags" : [ "TENNIS", "SPORT", "RACKET" ] }, { "itemCode" : "A00201", "name" : "WinWin Sport Shoe - Super", "brand" : "WinWin", "property" : { "size" : "35", "colors" : [ "RED" ] }, "qty" : 1, "unit" : "Pair", "unitPrice" : 110.0, "unitDiscount" : 10.0, "tags" : [ "SHOE", "SPORT", "WOMEN" ] } ]
Path chart
{} → items[] ⇒ [{}]
-
An array filter is enclosed by square brackets.
Directly query an array element by zero-based index value.josson.getNode("items[0]") ==> { "itemCode" : "B00001", "name" : "WinWin TShirt Series A - 2022", "brand" : "WinWin", "property" : { "size" : "M", "colors" : [ "WHITE", "RED" ] }, "qty" : 2, "unit" : "Pcs", "unitPrice" : 15.0, "tags" : [ "SHIRT", "WOMEN" ] }
Path chart
{} → items[#] ⇒ {}
-
To query a child value node of an array element.
josson.getNode("items[1].name") ==> "OctoPlus Tennis Racket - Star"
Path chart
{} → items[#] → {} → name ⇒ ""
-
To query a child object node of an array element.
josson.getNode("items[2].property") ==> { "size" : "35", "colors" : [ "RED" ] }
Path chart
{} → items[#] → {} → property{} ⇒ {}
-
To query all the elements of an array node and output them inside an array node.
josson.getNode("items.qty") ==> [ 2, 1, 1 ]
Path chart
{} → items[] → [{}] → [qty] ⇒ [$I]
-
A function that manipulates each array element and output all results inside an array node.
josson.getNode("items.concat('Qty=',qty)") ==> [ "Qty=2", "Qty=1", "Qty=1" ]
Path chart
{} → items[] → [{}] → [concat(%) ⇒ ""] ⇒ [""]
-
If a step is working on an object or value node,
?
represents that node.josson.getNode("items.qty.concat('Qty=',?)") ==> [ "Qty=2", "Qty=1", "Qty=1" ]
Path chart
{} → items[] → [{}] → [qty] → [concat(?) ⇒ ""] ⇒ [""]
-
An aggregate function manipulates an array node and produce a value node.
josson.getNode("items.qty.sum()") ==> 4.0
Path chart
{} → items[] → [{}] → [qty] → sum(?[]) ⇒ $D
-
Uses Java standard formatting pattern.
josson.getNode("items.sum(qty).formatNumber('#,##0')") ==> "4"
Path chart
{} → items[] → [{}] → sum(?[%]) → $D → formatNumber(?) ⇒ ""
-
Find the first matching element by array filter.
josson.getNode("items.itemCode[!startsWith('A')]") ==> "B00001"
Path chart
{} → items[] → [{}] → [itemCode][=] ⇒ ""
-
Filter using relational operators
=
,!=
,>
,>=
,<
and<=
.josson.getNode("items[unitDiscount > 0].name") ==> "OctoPlus Tennis Racket - Star"
Path chart
{} → items[=] → {} → name ⇒ ""
-
Returns null value if nothing matches the array filter.
josson.getNode("items[unitDiscount > 100].name") ==> !unresolvable!
Path chart
{} → items[=]!! → {} → name ⇒ !unresolvable!
-
To query all matching elements, add a modifier
*
after the array filter.josson.getNode("items[unitDiscount > 0]*.name") ==> [ "OctoPlus Tennis Racket - Star", "WinWin Sport Shoe - Super" ]
Path chart
{} → items[=]* → [{}] → [name] ⇒ [""]
-
If a step is working on an array node,
#
denotes the zero-based index of an array element.josson.getNode("items[#.isEven()]*.itemCode") ==> [ "B00001", "A00201" ]
Path chart
{} → items[=]* → [{}] → [itemCode] ⇒ [""]
-
A succession of two path steps that produced a nested array will be flattened automatically.
josson.getNode("items[true]*.tags[true]*") ==> [ "SHIRT", "WOMEN", "TENNIS", "SPORT", "RACKET", "SHOE", "SPORT", "WOMEN" ]
Path chart
{} → items[=]* → [{}] → [tags[=]* ⇒ [""]] ⇒ [""]
-
Path step
array.
is the same asarray[true]*.
.josson.getNode("items.tags") ==> [ "SHIRT", "WOMEN", "TENNIS", "SPORT", "RACKET", "SHOE", "SPORT", "WOMEN" ]
Path chart
{} → items[] → [{}] → [tags[] ⇒ [""]] ⇒ [""]
-
To simulate cancellation of the automatic flatten mechanism, add a divert-branch modifier "@" to the end of the first array name.
josson.getNode("[email protected]") ==> [ [ "SHIRT", "WOMEN" ], [ "TENNIS", "SPORT", "RACKET" ], [ "SHOE", "SPORT", "WOMEN" ] ]
Path chart
{} → tags[] → [""] / \ {} → items[] → []@ @ ⇒ [[""]] \ / {} → tags[] → [""]
-
Modifier
@
after a path step separator.
merges all branch results into a single array before manipulation.josson.getNode("items@.@tags") ==> [ "SHIRT", "WOMEN", "TENNIS", "SPORT", "RACKET", "SHOE", "SPORT", "WOMEN" ]
Path chart
{} / \ {} → items[] → []@ @ ⇒ [{}] → [tags[] ⇒ [""]] ⇒ [""] \ / {}
-
If a step is working on an array node,
?
represents an array element.
=~
matches a regular expression.josson.getNode("items.tags[? =~ '^S.*O.+']*") ==> [ "SPORT", "SHOE", "SPORT" ]
Path chart
{} → items[] → [{}] → [tags[=]* ⇒ [""]] ⇒ [""]
-
The matching criteria supports logical operators and parentheses.
not
!
and&
or|
josson.getNode("items[(unitDiscount=null | unitDiscount=0) & !(qty<=1)]*.name") ==> [ "WinWin TShirt Series A - 2022" ]
Path chart
{} → items[=]* → [{}] → [name] ⇒ [""]
-
Example of a find-all filter operation with flattened array result.
josson.getNode("items[tags.contains('SPORT')]*.tags") ==> [ "TENNIS", "SPORT", "RACKET", "SHOE", "SPORT", "WOMEN" ]
Path chart
{} → items[=]* → [{}] → [tags[] ⇒ [""]] ⇒ [""]
-
An array filter modifier
@
divert each element to separate branch for upcoming manipulation.
The final output merges branches into an array.josson.getNode("items[tags.containsIgnoreCase('Women')]@.tags") ==> [ [ "SHIRT", "WOMEN" ], [ "SHOE", "SPORT", "WOMEN" ] ]
Path chart
{} → tags[] → [""] / \ {} → items[=]@ @ ⇒ [[""]] \ / {} → tags[] → [""]
-
Aggregate functions work on an array node and produce a value node.
josson.getNode("items.tags.join('+')") ==> SHIRT+WOMEN+TENNIS+SPORT+RACKET+SHOE+SPORT+WOMEN
Path chart
{} → items[] → [{}] → [tags[] ⇒ [""]] → [""] → join(?[]) ⇒ ""
-
An array node can apply the modifier
@
that divert each element to separate branch.josson.getNode("[email protected]('+')") ==> [ "SHIRT+WOMEN", "TENNIS+SPORT+RACKET", "SHOE+SPORT+WOMEN" ]
Path chart
{} → tags[] → [""] → join(?[]) → "" / \ {} → items[] → []@ @ ⇒ [""] \ / {} → tags[] → [""] → join(?[]) → ""
-
Syntax
[]@
diverts each element of the current array node.josson.getNode("items.join([]@.tags.join('+'),' / ')") ==> "SHIRT+WOMEN / TENNIS+SPORT+RACKET / SHOE+SPORT+WOMEN"
Path chart
{} → tags[] → [""] → join(?[]) → "" / \ {} → items[] → [{}] → join(?[]@ @ ⇒ [""]) ⇒ "" \ / {} → tags[] → [""] → join(?[]) → ""
-
Modifier
@
before a function name merges all branch results into a single array before manipulation.josson.getNode("[email protected]('+').@join(' / ')") ==> "SHIRT+WOMEN / TENNIS+SPORT+RACKET / SHOE+SPORT+WOMEN"
Path chart
{} → tags[] → [""] → join(?[]) → "" / \ {} → items[] → []@ @ → [""] → join(?[]) ⇒ "" \ / {} → tags[] → [""] → join(?[]) → ""
-
Modifier
@
after a function diverts the function output array elements to separate branches.
It has the same effect of a path step.[]@
after a function.josson.getNode("'1+2 | 3+4 | 5+6'.split('|')@.split('+').calc(?*2).round(0).join('+').concat('(',?,')/2').@join(' | ')") ==> "(2+4)/2 | (6+8)/2 | (10+12)/2"
Path chart
"" → split(?) → [""] → [calc(?) ⇒ $D] → [round(?) ⇒ $I] → join(?[]) → "" → concat(?) → "" / \ "" → split(?) → [""]@ @ → [""] → join(?[]) ⇒ "" \ / "" → split(?) → [""] → [calc(?) ⇒ $D] → [round(?) ⇒ $I] → join(?[]) → "" → concat(?) → ""
-
All function parameters can refer to a child node of the step.
josson.getNode("[email protected](concat('[',brand,'] ',name,'\n'), qty).@join()") ==> "[WinWin] WinWin TShirt Series A - 2022\n" + "[WinWin] WinWin TShirt Series A - 2022\n" + "[OctoPlus] OctoPlus Tennis Racket - Star\n" + "[WinWin] WinWin Sport Shoe - Super\n"
Path chart
{} → repeat(%) → "" / \ {} → items[] → []@ @ → [""] → join(?[]) ⇒ "" \ / {} → repeat(%) → ""
-
Scalar functions work on array and produce an array, such as
concat()
, manipulate on each element.josson.getNode("items.concat('Item ',#,': [',itemCode,'] ',qty,unit,' x ',name,' <',property.colors.join(','),'>').join('\n')") ==> "Item 0: [B00001] 2Pcs x WinWin TShirt Series A - 2022 <WHITE,RED>\n" + "Item 1: [A00308] 1Pcs x OctoPlus Tennis Racket - Star <BLACK>\n" + "Item 2: [A00201] 1Pair x WinWin Sport Shoe - Super <RED>"
Path chart
{} → items[] → [{}] → [concat(%) ⇒ ""] → join(?[]) ⇒ ""
-
If a step is working on an array node,
@
represents that array node.
##
denotes the one-based index of an array element.josson.getNode("items.sort(itemCode).concat('Item ',##,'/',@.size(),': [',itemCode,'] ',qty,unit,' x ',name,' <',property.colors.join(','),'>').join('\n')") ==> "Item 1/3: [A00201] 1Pair x WinWin Sport Shoe - Super <RED>\n" + "Item 2/3: [A00308] 1Pcs x OctoPlus Tennis Racket - Star <BLACK>\n" + "Item 3/3: [B00001] 2Pcs x WinWin TShirt Series A - 2022 <WHITE,RED>"
Path chart
{} → items[] → [{}] -> sort(%) → [{}] → [concat(@, %) ⇒ ""] → join(?[]) ⇒ ""
-
An object node with a validation filter.
josson.getNode("customer[name='Peggy']") ==> { "customerId" : "CU0001", "name" : "Peggy", "phone" : "+852 62000610" }
Path chart
{} → customer{=} ⇒ {}
-
An object node that cannot meet the validation filter criteria returns null.
josson.getNode("customer[name='Raymond']") ==> !unresolvable!
Path chart
{} → customer{=}!! ⇒ !unresolvable!
-
In filter expression and function argument, a path starts with symbol "$" restart from the root node.
josson.getNode("items.concat($.customer.customerId, '-', itemCode)") ==> [ "CU0001-B00001", "CU0001-A00308", "CU0001-A00201" ]
Path chart
.------------- → --------------. | | {} → items[] → [{}] → [concat(%,$) ⇒ ""] ⇒ [""]
-
In filter expression and function argument, a path starts with symbol ".." go back to the previous step's node. Each additional dot go back one more step.
josson.getNode("items.property.concat(...customer.name, ' items=', ..size(), ' colors=', colors.join(','))") ==> [ "Peggy items=3 colors=WHITE,RED", "Peggy items=3 colors=BLACK", "Peggy items=3 colors=RED" ]
Path chart
.---------------------------- → ---------------. | .------------ → ------------. | | | | | {} → items[] → [{}] → [property] → [concat(%,..,...) ⇒ ""] ⇒ [""]
-
One more example.
josson.getNode("[email protected](....customer.name, ' ', ..itemCode, ' colors=', colors.join(','))") ==> [ "Peggy items=3 colors=WHITE,RED", "Peggy items=3 colors=BLACK", "Peggy items=3 colors=RED" ]
Path chart
.------------------------------ → --------------. | .----------- → -----------. | | | | | | {} → property{} → concat(%,..,....) → "" | / \ {} → items[] → []@ @ ⇒ [""] \ / {} → property{} → concat(%,..,....) → ""
-
Function
json()
parse a JSON string.josson.getNode("json('[1,2,"3"]')") ==> [ 1, 2, "3" ]
Path chart
json("") ⇒ []
-
Relational operator
=
and!=
support object comparison.josson.getNode("[customer = json('{"name":"Peggy","phone":"+852 62000610","customerId":"CU0001"}')].isNotNull()") ==> true
Path chart
{} → {=} → {} → isNotNull(?) ⇒ $TF
-
Relational operator
=
and!=
support root level array values comparison where the position ordering is allowed to be different.josson.getNode("[items[0].property.colors = json('["RED","WHITE"]')].isNotNull()") ==> true
Path chart
{} → {=} → {} → isNotNull(?) ⇒ $TF
-
Function
calc()
uses MathParser.org-mXparser library http://mathparser.org/ to perform calculation.josson.getNode("items.calc(qty * (unitPrice-unitDiscount)).concat(##,'=',?)") ==> [ null, "2=140.0", "3=100.0" ]
Path chart
{} → items[] → [{}] → [calc(%) ⇒ $D] → [concat(?) ⇒ ""] ⇒ [""]
-
Scalar functions preserve null element.
josson.getNode("items.calc(qty * (unitPrice-unitDiscount)).[##<=2]*.concat(##,'=',?)") ==> [ null, "2=140.0" ]
Path chart
{} → items[] → [{}] → [calc(%) ⇒ $D] → [=]* → [concat(?) ⇒ ""] ⇒ [""]
-
An array-to-value transformation function throws away null nodes automatically.
josson.getNode("items.calc(qty * (unitPrice-unitDiscount)).concat(##,'=',?).join(' / ')") ==> "2=140.0 / 3=100.0"
Path chart
{} → items[] → [{}] → [calc(%) ⇒ $D] → [concat(?) ⇒ ""] → join(?[]) ⇒ ""
-
Array filter can filter out null nodes.
josson.getNode("items.calc(qty * (unitPrice-unitDiscount)).[isNotNull()]*.concat(##,'=',?)") ==> [ "1=140.0", "2=100.0" ]
Path chart
{} → items[] → [{}] → [calc(%) ⇒ $D] → [=]* → [concat(?) ⇒ ""] ⇒ [""]
-
An argument
#A
denotes the uppercase alphabetic array index.josson.getNode("items.calc(qty * (unitPrice-unitDiscount)).[?!=null]*.concat(#A,'=',?).join(' / ')") ==> "A=140.0 / B=100.0"
Path chart
{} → items[] → [{}] → [calc(%) ⇒ $D] → [=]* → [concat(?) ⇒ ""] → join(?[]) ⇒ ""
-
Merge Diverted branches throws away null nodes automatically. An argument
#a
denotes the lowercase alphabetic array index.josson.getNode("[email protected](qty * (unitPrice-unitDiscount)).@concat(#a,'=',?)") ==> [ "a=140.0", "b=100.0" ]
Path chart
{} → calc(%) → $D / \ {} → items[] → []@ @ → [$D] → [concat(?) ⇒ ""] ⇒ [""] \ / {} → calc(%) → $D
-
mXparser expression accepts single-step path only. To apply multi-steps path, function or filter, append arguments with syntax
newVariable:path
.josson.getNode("items.calc(qty * (unitPrice-x), x:coalesce(unitDiscount,0)).formatNumber('US$#,##0.00')") ==> [ "US$30.00", "US$140.00", "US$100.00" ]
Path chart
{} → items[] → [{}] → [calc(%) ⇒ $D] → [formatNumber(?) ⇒ ""] ⇒ [""]
-
An argument
#r
and#R
denotes the lowercase and uppercase roman numerals array index.josson.getNode("items.unitPrice.calc(? * 2).concat(#r,'=',?)") ==> [ "i=30.0", "ii=300.0", "iii=220.0" ]
Path chart
{} → items[] → [{}] → [unitPrice ⇒ $D] → [calc(?) ⇒ $D] → [concat(?) ⇒ ""] ⇒ [""]
-
Function
entries()
returns an array of an object's string-keyed property[{key, value}]
pairs.josson.getNode("items[0].entries()") ==> [ { "key" : "itemCode", "value" : "B00001" }, { "key" : "name", "value" : "WinWin TShirt Series A - 2022" }, { "key" : "brand", "value" : "WinWin" }, { "key" : "property", "value" : { "size" : "M", "colors" : [ "WHITE", "RED" ] } }, { "key" : "qty", "value" : 2 }, { "key" : "unit", "value" : "Pcs" }, { "key" : "unitPrice", "value" : 15.0 }, { "key" : "tags", "value" : [ "SHIRT", "WOMEN" ] } ]
Path chart
{} → items[#] → {} → entries(?) ⇒ [{}]
-
Function
keys()
lists an object's key names.josson.getNode("keys()") ==> [ "salesOrderId", "salesDate", "salesPerson", "customer", "items", "totalAmount" ]
Path chart
{} → keys(?) ⇒ [""]
-
keys()
can retrieve nested child object keys for a given levels.josson.getNode("keys(2)") ==> [ "salesOrderId", "salesDate", "salesPerson", "customer", "customerId", "name", "phone", "items", "totalAmount" ]
Path chart
{} → keys(?) ⇒ [""]
-
Function
collect()
puts all argument values into an array. Functionwrap()
is equivalent tocollect(?)
which is wrap the node inside an array.josson.getNode("collect(salesDate, customer, items.itemCode)") ==> [ "2022-01-01T10:01:23", { "customerId" : "CU0001", "name" : "Peggy", "phone" : "+852 62000610" }, [ "B00001", "A00308", "A00201" ] ]
Path chart
{} → collect(%) ⇒ []
-
Function
cumulateCollect()
require 2 arguments. The 1st parameter is a query to evaluate a result that will be collected into an array. The 2nd parameter is a query to evaluate the next dataset that loop back for the 1st parameter evaluation again. The operation loop will be stopped when the next dataset is null.josson.getNode("json('{\"id\":1,\"val\":11,\"item\":{\"id\":2,\"val\":22,\"item\":{\"id\":3,\"val\":33,\"item\":{\"id\":4,\"val\":44}}}}')" + ".cumulateCollect(map(id,val.calc(?*2)), item)") ==> [ { "id" : 1, "val" : 22.0 }, { "id" : 2, "val" : 44.0 }, { "id" : 3, "val" : 66.0 }, { "id" : 4, "val" : 88.0 } ]
Path chart
{} → cumulateCollect(%) ⇒ [{}]
-
Function
toArray()
puts an object's values into an array.josson.getNode("customer.toArray()") ==> [ "CU0001", "Peggy", "+852 62000610" ]
Path chart
{} → customer{} → toArray(?) ⇒ [""]
-
Furthermore, function
toArray()
puts all arguments (values, object's values, array elements) into a single array.josson.getNode("toArray('Hello',customer,items.itemCode.sort())") ==> [ "Hello", "CU0001", "Peggy", "+852 62000610", "A00201", "A00308", "B00001" ]
Path chart
{} → toArray(%) ⇒ [""]
-
Function
map()
constructs a new object node. For multi-steps path, the last element name will become the new element name. To rename an element, use syntaxnewFieldName:path
orqueryThatResolveToName::path
.josson.getNode("map(customer.name,date:salesDate,sales:map(items.concat(name,' x ',qty,unit), totalQty:items.sum(qty), totalAmount))") ==> { "name" : "Peggy", "date" : "2022-01-01T10:01:23", "sales" : { "items" : [ "WinWin TShirt Series A - 2022 x 2Pcs", "OctoPlus Tennis Racket - Star x 1Pcs", "WinWin Sport Shoe - Super x 1Pair" ], "totalQty" : 4.0, "totalAmount" : 270.0 } }
Path chart
{} → map(%) ⇒ {}
-
Function
field()
adds, removes and renames field on the current object node. To remove an element, use syntaxfieldName:
orqueryThatResolveToName::
.josson.getNode("items[0].field(subtotal:calc(qty * (unitPrice-x), x:coalesce(unitDiscount,0)),brand:,property:,tags:)") ==> { "itemCode" : "B00001", "name" : "WinWin TShirt Series A - 2022", "qty" : 2, "unit" : "Pcs", "unitPrice" : 15.0, "subtotal" : 30.0 }
Path chart
{} → items[#] → {} → field(%) ⇒ {}
-
Functions
map()
andfield()
works on array.josson.getNode("items.field(subtotal:calc(qty * (unitPrice-x), x:coalesce(unitDiscount,0)),brand:,property:,tags:)") ==> [ { "itemCode" : "B00001", "name" : "WinWin TShirt Series A - 2022", "qty" : 2, "unit" : "Pcs", "unitPrice" : 15.0, "subtotal" : 30.0 }, { "itemCode" : "A00308", "name" : "OctoPlus Tennis Racket - Star", "qty" : 1, "unit" : "Pcs", "unitPrice" : 150.0, "unitDiscount" : 10.0, "subtotal" : 140.0 }, { "itemCode" : "A00201", "name" : "WinWin Sport Shoe - Super", "qty" : 1, "unit" : "Pair", "unitPrice" : 110.0, "unitDiscount" : 10.0, "subtotal" : 100.0 } ]
Path chart
{} → items[] → [{}] → [field(%) ⇒ {}] ⇒ [{}]
-
For functions
map()
andfield()
, parameter syntaxpath:+
present an unresolvable path as a NullNode.josson.getNode("items.map(itemCode, unitDiscount)") ==> [ { "itemCode" : "B00001", }, { "itemCode" : "A00308", "unitDiscount" : 10.0 }, { "itemCode" : "A00201", "unitDiscount" : 10.0 } ] josson.getNode("items.map(itemCode, unitDiscount:+)") ==> [ { "itemCode" : "B00001", "unitDiscount" : null }, { "itemCode" : "A00308", "unitDiscount" : 10.0 }, { "itemCode" : "A00201", "unitDiscount" : 10.0 } ] josson.getNode("items.map(itemCode, unitDiscount:if([unitDiscount=null],null,unitDiscount))") ==> [ { "itemCode" : "B00001", "unitDiscount" : null }, { "itemCode" : "A00308", "unitDiscount" : 10.0 }, { "itemCode" : "A00201", "unitDiscount" : 10.0 } ]
-
For functions
map()
andfield()
, parameter syntaxnewFieldName:+path
present an unresolvable path as a NullNode with a new field name.josson.getNode("items.map(itemCode, discount:+unitDiscount)") ==> [ { "itemCode" : "B00001", "discount" : null }, { "itemCode" : "A00308", "discount" : 10.0 }, { "itemCode" : "A00201", "discount" : 10.0 } ]
-
For functions
map()
andfield()
, parameter syntax**:object
extracts all the fields within a given object.josson.getNode("items.slice(0,2).field(**:properties, properties:)") ==> [ { "itemCode" : "B00001", "name" : "WinWin TShirt Series A - 2022", "brand" : "WinWin", "qty" : 2, "unit" : "Pcs", "unitPrice" : 15.0, "tags" : [ "SHIRT", "WOMEN" ], "size" : "M", "colors" : [ "WHITE", "RED" ] }, { "itemCode" : "A00308", "name" : "OctoPlus Tennis Racket - Star", "brand" : "OctoPlus", "qty" : 1, "unit" : "Pcs", "unitPrice" : 150.0, "unitDiscount" : 10.0, "tags" : [ "TENNIS", "SPORT", "RACKET" ], "colors" : [ "BLACK" ] } ]
-
Function
group()
works like SQLgroup by
. It will build a structure of[{key, [elements]}]
. The first parameter is the grouping key. If it is a function, it will be given a namekey
in the output. The optional second parameter is to evaluate the grouped element. The default is the whole array element. And the default output array name iselements
. The names can be renamed by preceding withnewName:
orqueryThatResovleToName::
.josson.getNode("items.group(brand,map(name,qty,netPrice:calc(unitPrice-x,x:coalesce(unitDiscount,0))))") ==> [ { "brand" : "WinWin", "elements" : [ { "name" : "WinWin TShirt Series A - 2022", "qty" : 2, "netPrice" : 15.0 }, { "name" : "WinWin Sport Shoe - Super", "qty" : 1, "netPrice" : 100.0 } ] }, { "brand" : "OctoPlus", "elements" : [ { "name" : "OctoPlus Tennis Racket - Star", "qty" : 1, "netPrice" : 140.0 } ] } ] josson.getNode( "items.group(brand,map(name,qty,netPrice:calc(unitPrice-x,x:coalesce(unitDiscount,0))))@" + ".concat('Brand : ',brand,'\n',elements.concat('- ',name,' : Qty=',qty,' Amt=',calc(qty*netPrice),'\n').join()," + "'> Sub-total : Qty=',elements.sum(qty),' Amt=',elements.sum(calc(qty*netPrice))).@join('\n\n')") ==> Brand : WinWin - WinWin TShirt Series A - 2022 : Qty=2 Amt=30.0 - WinWin Sport Shoe - Super : Qty=1 Amt=100.0 > Sub-total : Qty=3.0 Amt=130.0 Brand : OctoPlus - OctoPlus Tennis Racket - Star : Qty=1 Amt=140.0 > Sub-total : Qty=1.0 Amt=140.0
Path chart
{} → concat(%) → "" / \ {} → items[] → [{}] → group(%) → [{}]@ @ → [""] → join(?[]) ⇒ "" \ / {} → concat(%) → ""
-
Function
unwind()
works like MongoDB$unwind
operation. The operation is the reverse ofgroup()
.josson.getNode("items.group(brand,map(name,qty,netPrice:calc(unitPrice-x,x:coalesce(unitDiscount,0)))).unwind(elements)") ==> [ { "brand" : "WinWin", "name" : "WinWin TShirt Series A - 2022", "qty" : 2, "netPrice" : 15.0 }, { "brand" : "WinWin", "name" : "WinWin Sport Shoe - Super", "qty" : 1, "netPrice" : 100.0 }, { "brand" : "OctoPlus", "name" : "OctoPlus Tennis Racket - Star", "qty" : 1, "netPrice" : 140.0 } ]
Path chart
{} → items[] → [{}] → group(%) → [{}] → unwind(%) ⇒ [{}]
-
Function
flatten()
flatten an array for a given number of times.josson.getNode("[email protected]") ==> [ [ "SHIRT", "WOMEN" ], [ "TENNIS", "SPORT", "RACKET" ], [ "SHOE", "SPORT", "WOMEN" ] ] flatten([email protected], 1, null) ==> [ "SHIRT", "WOMEN", "TENNIS", "SPORT", "RACKET", "SHOE", "SPORT", "WOMEN" ]
-
Function
flatten()
flatten an array same as the default path step behavior. But more readable.josson.getNode("[email protected].@.[true]*") ==> [ "SHIRT", "WOMEN", "TENNIS", "SPORT", "RACKET", "SHOE", "SPORT", "WOMEN" ] josson.getNode("[email protected][email protected](1)") ==> [ "SHIRT", "WOMEN", "TENNIS", "SPORT", "RACKET", "SHOE", "SPORT", "WOMEN" ]
Path chart
{} → tags[] → [""] / \ {} → items[] → []@ @ → [[""]] → flatten() ⇒ [""] \ / {} → tags[] → [""]
-
If the parameter value of
flatten()
is textual, it will act as a key name separator to build a flattened object. If the separator isnull
, the standalone end node name will be used instead. The second parameter is optional and is the string format for array index, usually use with this combination('.', '[%d]')
.josson.getNode("flatten('_')") ==> { "salesOrderId" : "SO0001", "salesDate" : "2022-01-01T10:01:23", "salesPerson" : "Raymond", "customer_customerId" : "CU0001", "customer_name" : "Peggy", "customer_phone" : "+852 62000610", "items_0_itemCode" : "B00001", "items_0_name" : "WinWin TShirt Series A - 2022", "items_0_brand" : "WinWin", "items_0_property_size" : "M", "items_0_property_colors_0" : "WHITE", "items_0_property_colors_1" : "RED", "items_0_qty" : 2, "items_0_unit" : "Pcs", "items_0_unitPrice" : 15.0, "items_0_tags_0" : "SHIRT", "items_0_tags_1" : "WOMEN", "items_1_itemCode" : "A00308", "items_1_name" : "OctoPlus Tennis Racket - Star", "items_1_brand" : "OctoPlus", "items_1_property_colors_0" : "BLACK", "items_1_qty" : 1, "items_1_unit" : "Pcs", "items_1_unitPrice" : 150.0, "items_1_unitDiscount" : 10.0, "items_1_tags_0" : "TENNIS", "items_1_tags_1" : "SPORT", "items_1_tags_2" : "RACKET", "items_2_itemCode" : "A00201", "items_2_name" : "WinWin Sport Shoe - Super", "items_2_brand" : "WinWin", "items_2_property_size" : "35", "items_2_property_colors_0" : "RED", "items_2_qty" : 1, "items_2_unit" : "Pair", "items_2_unitPrice" : 110.0, "items_2_unitDiscount" : 10.0, "items_2_tags_0" : "SHOE", "items_2_tags_1" : "SPORT", "items_2_tags_2" : "WOMEN", "totalAmount" : 270.0 }
-
Function
unflatten()
reverse the operation offlatten()
.josson.getNode("items[1].flatten('_').unflatten('_')") ==> { "itemCode" : "A00308", "name" : "OctoPlus Tennis Racket - Star", "brand" : "OctoPlus", "property" : { "colors" : [ "BLACK" ] }, "qty" : 1, "unit" : "Pcs", "unitPrice" : 150.0, "unitDiscount" : 10.0, "tags" : [ "TENNIS", "SPORT", "RACKET" ] }
-
Functions
map()
,field()
,group()
,unwind()
- key name support evaluation using syntaxkeyQuery::valueQuery
.josson.getNode("items.map(itemCode::qty)") ==> [ { "B00001" : 2 }, { "A00308" : 1 }, { "A00201" : 1 } ]
Path chart
{} → items[] → [{}] → [map(%) ⇒ {}] ⇒ [{}]
-
Syntax
keyQuery::+valueQuery
present an unresolvable path as a NullNode.josson.getNode("items.map(itemCode::unitDiscount)") ==> [ { }, { "A00308" : 10.0 }, { "A00201" : 10.0 } ] josson.getNode("items.map(itemCode::+unitDiscount)") ==> [ { "B00001" : null }, { "A00308" : 10.0 }, { "A00201" : 10.0 } ]
-
Function
mergeObjects()
merge all objects in an array into one object.josson.getNode("mergeObjects(customer, items.map(itemCode::qty))") ==> { "customerId" : "CU0001", "name" : "Peggy", "phone" : "+852 62000610", "B00001" : 2, "A00308" : 1, "A00201" : 1 }
Path chart
{} → mergeObjects(%) ⇒ {}
-
Function
assort()
separates an object's entries according to different path conditions in sequence, and put them into the corresponding array if the evaluated result is not null. Entries will be removed if no condition can be matched. If the last argument is??
, each of the remaining entry will be added to the end of result array separately. If no argument is provided, each entry will be added to the result array separately.josson.getNode("json('{\"xy1\": 1,\"xy2\": 2,\"ab1\": 3,\"ab2\": 4,\"ab3\": 5,\"zz1\": 6,\"xy3\": 7,\"zz2\": 9,\"zz3\": {\"k\":10}}}')" + ".assort(*.[isEven()], ~'xy.*', ~'ab.*', *)") ==> [ { "xy2" : 2, "ab2" : 4, "zz1" : 6 }, { "xy1" : 1, "xy3" : 7 }, { "ab1" : 3, "ab3" : 5 }, { "zz2" : 9, "zz3" : { "k" : 10 } } ] josson.getNode("json('{\"xy1\": 1,\"xy2\": 2,\"ab1\": 3,\"ab2\": 4,\"ab3\": 5,\"zz1\": 6,\"xy3\": 7,\"zz2\": 9,\"zz3\": {\"k\":10}}')" + ".assort(*.[isEven()], ~'xy.*', ~'ab.*')") ==> [ { "xy2" : 2, "ab2" : 4, "zz1" : 6 }, { "xy1" : 1, "xy3" : 7 }, { "ab1" : 3, "ab3" : 5 } ] josson.getNode("json('{\"xy1\": 1,\"xy2\": 2,\"ab1\": 3,\"ab2\": 4,\"ab3\": 5,\"zz1\": 6,\"xy3\": 7,\"zz2\": 9,\"zz3\": {\"k\":10}}}')" + ".assort(*.[isEven()], ~'xy.*', ~'ab.*', ??)") ==> [ { "xy2" : 2, "ab2" : 4, "zz1" : 6 }, { "xy1" : 1, "xy3" : 7 }, { "ab1" : 3, "ab3" : 5 }, { "zz2" : 9 }, { "zz3" : { "k" : 10 } } ] josson.getNode("json('{\"xy1\": 1,\"xy2\": 2,\"ab1\": 3,\"ab2\": 4,\"ab3\": 5,\"zz1\": 6,\"xy3\": 7,\"zz2\": 9,\"zz3\": {\"k\":10}}}')" + ".assort()") ==> [ { "xy1" : 1 }, { "xy2" : 2 }, { "ab1" : 3 }, { "ab2" : 4 }, { "ab3" : 5 }, { "zz1" : 6 }, { "xy3" : 7 }, { "zz2" : 9 }, { "zz3" : { "k" : 10 } } ]
-
Function
assort()
also works for array. The result is an array of arrays.josson.getNode("json('[1,2,3,4,5,6,7,8,9,10,11,12]').assort([?<5], [isEven()], [?<9], ?)") ==> [ [ 1, 2, 3, 4 ], [ 6, 8, 10, 12 ], [ 5, 7 ], [ 9, 11 ] ] josson.getNode("json('[1,2,3,4,5,6,7,8,9,10,11,12]').assort([?<5], [isEven()], [?<9], ??)") ==> [ [ 1, 2, 3, 4 ], [ 6, 8, 10, 12 ], [ 5, 7 ], [ 9 ], [ 11 ] ]
-
Function
eval()
evaluates the value of a text node as a query statement.josson.getNode("json('{"a":1,"b":2,"statement":"calc(a+b*2)"}').eval(statement)") ==> 5.0 josson.getNode("json('[{"a":3,"s":"calc(a*2)"},{"a":4,"s":"calc(a*2)"}]')@.eval(s)") ==> [ 6.0, 8.0 ]
There are 245 functions. They are classified into 7 categories:
Arithmetic Functions
String Functions
- abbreviate()
- append()
- appendIfMissing()
- appendIfMissingIgnoreCase()
- capitalize()
- center()
- concat()
- concatFree()
- eval()
- keepAfter()
- keepAfterIgnoreCase()
- keepAfterLast()
- keepAfterLastIgnoreCase()
- keepBefore()
- keepBeforeIgnoreCase()
- keepBeforeLast()
- keepBeforeLastIgnoreCase()
- leftPad()
- length()
- lowerCase()
- notEmpty()
- notBlank()
- prepend()
- prependIfMissing()
- prependIfMissingIgnoreCase()
- removeEnd()
- removeEndIgnoreCase()
- removeStart()
- removeStartIgnoreCase()
- repeat()
- replace()
- replaceIgnoreCase()
- rightPad()
- split()
- splitMax()
- separate()
- separateMax()
- strip()
- stripEnd()
- stripStart()
- substr()
- trim()
- uncapitalize()
- upperCase()
- camelCase()
- upperCamelCase()
- snakeCase()
- lowerSnakeCase()
- upperSnakeCase()
- camelSnakeCase()
- singleQuote() / quote() / q()
- doubleQuote() / qq()
Date Functions
- amPmOfDay()
- second()
- secondOfDay()
- minute()
- minuteOfDay()
- hourOfAmPm()
- hour()
- dayOfWeek()
- day()
- dayOfYear()
- month()
- year()
- plusSeconds()
- plusMinutes()
- plusHours()
- plusDays()
- plusWeeks()
- plusMonths()
- plusYears()
- minusSeconds()
- minusMinutes()
- minusHours()
- minusDays()
- minusWeeks()
- minusMonths()
- minusYears()
- truncateToMicro()
- truncateToMilli()
- truncateToSecond()
- truncateToMinute()
- truncateToHour()
- truncateToDay()
- truncateToMonth()
- truncateToYear()
- withNano()
- withMicro()
- withMilli()
- withSecond()
- withMinute()
- withHour()
- withDay()
- withDayOfYear()
- withMonth()
- withYear()
- dayEnd()
- monthEnd()
- yearEnd()
- lengthOfMonth()
- lengthOfYear()
- untilInSecond()
- untilInMinute()
- untilInHour()
- untilInDay()
- untilInMonth()
- untilInYear()
- localToOffsetDate()
- offsetToLocalDate()
- localDateToMillis()
- millisToLocalDate()
- localDateToSeconds()
- secondsToLocalDate()
- offsetDateToMillis()
- millisToOffsetDate()
- offsetDateToSeconds()
- secondsToOffsetDate()
- now()
Format Functions
- b64Encode()
- b64EncodeNoPadding()
- b64MimeEncode()
- b64MimeEncodeNoPadding()
- b64UrlEncode()
- b64UrlEncodeNoPadding()
- b64Decode()
- b64MimeDecode()
- b64UrlDecode()
- urlEncode()
- urlDecode()
- escapeHtml()
- unescapeHtml()
- escapeXml()
- unescapeXml()
- if()
- ifNot()
- coalesce()
- caseValue()
- caseValueIgnoreCase()
- cycleValue()
- default()
- indexedValue()
- formatDate()
- formatNumber()
- formatText()
- formatTexts()
- toNumber()
- toString()
- toText()
- csv()
- csvShowNull()
- csvParams()
Logical Functions
- contains()
- containsIgnoreCase()
- notContains()
- notContainsIgnoreCase()
- startsWith()
- startsWithIgnoreCase()
- notStartsWith()
- notStartsWithIgnoreCase()
- endsWith()
- endsWithIgnoreCase()
- notEndsWith()
- notEndsWithIgnoreCase()
- equals()
- equalsIgnoreCase()
- notEquals()
- notEqualsIgnoreCase()
- matches()
- notMatches()
- in()
- inIgnoreCase()
- notIn()
- notInIgnoreCase()
- isEmpty()
- isNotEmpty()
- isBlank()
- isNotBlank()
- isNull()
- isNotNull()
- isText()
- isBoolean()
- isNumber()
- isEven()
- isOdd()
- isArray()
- isObject()
- isEmptyArray()
- isEmptyObject
- not()
- isWeekday()
- isWeekend()
- isLeapYear()
Array Functions
- size()
- lastIndex()
- indexOf()
- lastIndexOf()
- first()
- last()
- max()
- min()
- topN()
- bottomN()
- sum()
- avg()
- count()
- push()
- reverse()
- slice()
- sort()
- distinct()
- join()
- findAndModify()
- findByMax()
- findByMin()
- findByNullOrMax()
- findByNullOrMin()
- findByMaxOrNull()
- findByMinOrNull()
Structural Functions
- json()
- let()
- get()
- entries()
- keys()
- depthLimit()
- collect()
- cumulateCollect()
- wrap()
- toArray()
- toObject()
- mergeArrays()
- mergeObjects()
- flatten()
- unflatten()
- map()
- field()
- group()
- unwind()
- assort()
- steps()
Following are some examples of each function.
-3.14.abs() ==> 3.14
abs(3.14) ==> 3.14
1.5.calc(? * 2 + ? / 2) ==> 3.75
calc(2^8) ==> 256.0
calc(sqrt(a^2 + b^2), a:3, b:4) ==> 5.0
3.14.ceil() ==> 4
ceil(-3.14) ==> -3
3.14.floor() ==> 3
floor(-3.14) ==> -4
8.mod(3) ==> 2
8.mod(?, 3) ==> 2
mod(-8, 3) ==> 1
3.mod(-8, ?) ==> 1
3.14.round(1) ==> 3.1
3.14.round(?, 1) ==> 3.1
round(3.56, 0) ==> 4
'abcdefghijkl'.abbreviate(9) ==> "abcdef..."
'abcdefghijkl'.abbreviate(5, 9) ==> "...fgh..."
'abcdefghijkl'.abbreviate(?, 7, 9) ==> "...ghijkl"
abbreviate('abcdefghijkl', 0, 9) ==> "abcdef..."
abbreviate('abcdefghijkl', 1, 9) ==> "abcdef..."
abbreviate('abcdefghijkl', 4, 9) ==> "abcdef..."
abbreviate('abcdefghijkl', 5, 9) ==> "...fgh..."
abbreviate('abcdefghijkl', 6, 9) ==> "...ghijkl"
abbreviate('abcdefghijkl', 10, 9) ==> "...ghijkl"
abbreviate('abcdefghijkl', 11, 9) ==> "...ghijkl"
'abc'.append('xyz') ==> "abcxyz"
'abc'.append(?, 'xyz') ==> "abcxyz"
append('abcxyz', 'xyz') ==> "abcxyzxyz"
'xyz'.append('abcXYZ', ?) ==> "abcXYZxyz"
'abc'.appendIfMissing('xyz') ==> "abcxyz"
'abc'.appendIfMissing(?, 'xyz') ==> "abcxyz"
appendIfMissing('abcxyz', 'xyz') ==> "abcxyz"
'xyz'.appendIfMissing('abcXYZ', ?) ==> "abcXYZxyz"
'abc'.appendIfMissingIgnoreCase('xyz') ==> "abcxyz"
'abc'.appendIfMissingIgnoreCase(?, 'xyz') ==> "abcxyz"
appendIfMissingIgnoreCase('abcxyz', 'xyz') ==> "abcxyz"
'xyz'.appendIfMissingIgnoreCase('abcXYZ', ?) ==> "abcXYZ"
'cat'.capitalize() ==> "Cat"
capitalize('cAt') ==> "CAt"
'abc'.center(7) ==> " abc "
'abc'.center(7, 'X') ==> "XXabcXX"
'abc'.center(?, 7, upperCase(?)) ==> "ABabcAB"
center('abc', 7, '') ==> " abc "
4.center('a', ?, 'yz') ==> "yayz"
'Hello'.concat(2022, '... ', ?, ' World!') ==> "2022... Hello World!"
json('{"a":"Hello","c":" World!"}').concat(a,b,c) ==> !unresolvable!
'Hello'.concatFree(2022, '... ', ?, ' World!') ==> "2022... Hello World!"
json('{"a":"Hello","c":" World!"}').concatFree(a,b,c) ==> "Hello World!"
json('{"a":1,"b":2,"statement":"calc(a+b*2)"}').eval(statement) ==> 5.0
json('[{"a":3,"s":"calc(a*2)"},{"a":4,"s":"calc(a*2)"}]')@.eval(s) ==> [ 6.0, 8.0 ]
'abcxmnxyz'.keepAfter('x') ==> "mnxyz"
'abcxmnxyz'.keepAfter(?, 'X') ==> ""
keepAfter('abcxmnxyz', 'mn') ==> "xyz"
'abcxmnxyz'.keepAfterIgnoreCase('x') ==> "mnxyz"
'abcxmnxyz'.keepAfterIgnoreCase(?, 'X') ==> "mnxyz"
keepAfterIgnoreCase('abcxmnxyz', 'mn') ==> "xyz"
'abcxmnxyz'.keepAfterLast('x') ==> "yz"
'abcxmnxyz'.keepAfterLast(?, 'X') ==> ""
keepAfterLast('abcxmnxyz', 'mn') ==> "xyz"
'abcxmnxyz'.keepAfterLastIgnoreCase('x') ==> "yz"
'abcxmnxyz'.keepAfterLastIgnoreCase(?, 'X') ==> "yz"
keepAfterLastIgnoreCase('abcxmnxyz', 'mn') ==> "xyz"
'abcxmnxyz'.keepBefore('x') ==> "abc"
'abcxmnxyz'.keepBefore(?, 'X') ==> ""
keepBefore('abcxmnxyz', 'mn') ==> "abcx"
'abcxmnxyz'.keepBeforeIgnoreCase('x') ==> "abc"
'abcxmnxyz'.keepBeforeIgnoreCase(?, 'X') ==> "abc"
keepBeforeIgnoreCase('abcxmnxyz', 'mn') ==> "abcx"
'abcxmnxyz'.keepBeforeLast('x') ==> "abcxmn"
'abcxmnxyz'.keepBeforeLast(?, 'X') ==> ""
keepBeforeLast('abcxmnxyz', 'mn') ==> "abcx"
'abcxmnxyz'.keepBeforeLastIgnoreCase('x') ==> "abcxmn"
'abcxmnxyz'.keepBeforeLastIgnoreCase(?, 'X') ==> "abcxmn"
keepBeforeLastIgnoreCase('abcxmnxyz', 'mn') ==> "abcx"
'bat'.leftPad(5) ==> " bat"
'bat'.leftPad(?, 8, 'yz') ==> "yzyzybat"
leftPad('bat', 3, 'yz') ==> "bat"
5.leftPad('bat', ?, '') ==> " bat"
'Josson'.length() ==> 6
length('Josson') ==> 6
length(2022) ==> 4
'Cat'.lowerCase() ==> "cat"
lowerCase('cAt') ==> "cat"
'abc'.notEmpty('xyz') ==> "abc"
''.notEmpty(null, '', 'xyz') ==> "xyz"
json('{"a":"","b":"","c":"abc"}').notEmpty(a,b,c,'xyz') ==> "abc"
'abc'.notBlank('xyz') ==> "abc"
' '.notBlank(null, ' ', 'xyz') ==> "xyz"
json('{"a":" ","b":" ","c":"abc"}').notBlank(a,b,c,'xyz') ==> "abc"
'abc'.prepend('xyz') ==> "xyzabc"
'abc'.prepend(?, 'xyz') ==> "xyzabc"
prepend('xyzabc', 'xyz') ==> "xyzxyzabc"
'xyz'.prepend('XYZabc', ?) ==> "xyzXYZabc"
'abc'.prependIfMissing('xyz') ==> "xyzabc"
'abc'.prependIfMissing(?, 'xyz') ==> "xyzabc"
prependIfMissing('xyzabc', 'xyz') ==> "xyzabc"
'xyz'.prependIfMissing('XYZabc', ?) ==> "xyzXYZabc"
'abc'.prependIfMissingIgnoreCase('xyz') ==> "xyzabc"
'abc'.prependIfMissingIgnoreCase(?, 'xyz') ==> "xyzabc"
prependIfMissingIgnoreCase('xyzabc', 'xyz') ==> "xyzabc"
'xyz'.prependIfMissingIgnoreCase('XYZabc', ?) ==> "XYZabc"
'www.domain.com'.removeEnd('.com') ==> "www.domain"
'www.domain.com'.removeEnd(?, '.Com') ==> "www.domain.com"
removeEnd('www.domain.com', '.com') ==> "www.domain"
'www.domain.COM'.removeEndIgnoreCase('.com') ==> "www.domain"
'www.domain.com'.removeEndIgnoreCase(?, '.Com') ==> "www.domain"
removeEndIgnoreCase('www.domain.com', '.COM') ==> "www.domain"
'www.domain.com'.removeStart('www.') ==> "domain.com"
'www.domain.com'.removeStart(?, '.Www') ==> "www.domain.com"
removeStart('www.domain.com', 'www.') ==> "domain.com"
'WWW.domain.com'.removeStartIgnoreCase('www.') ==> "domain.com"
'www.domain.com'.removeStartIgnoreCase(?, '.Www') ==> "www.domain.com"
removeStartIgnoreCase('www.domain.com', 'WWW.') ==> "domain.com"
'a'.repeat(3) ==> "aaa"
'ab'.repeat(?, 2) ==> "abab"
repeat('abc', 2) ==> "abcabc"
3.repeat('abc', ?) ==> "abcabcabc"
'abaa'.replace('a', 'z') ==> "zbzz"
'abaa'.replace(?, 'a', 'z', -1) ==> "zbzz"
replace('abaa', 'a', '', -1) ==> "b"
replace('abaa', 'A', 'z', 1) ==> "abaa"
'a'.replace('abaa', ?, 'z', 2) ==> "zbza"
'abaa'.replaceIgnoreCase('a', 'z') ==> "zbzz"
'abaa'.replaceIgnoreCase(?, 'a', 'z', -1) ==> "zbzz"
replaceIgnoreCase('abaa', 'a', '', -1) ==> "b"
replaceIgnoreCase('abaa', 'A', 'z', 1) ==> "zbaa"
'a'.replaceIgnoreCase('abaa', ?, 'z', 2) ==> "zbza"
'bat'.rightPad(5) ==> "bat "
'bat'.rightPad(?, 8, 'yz') ==> "batyzyzy"
rightPad('bat', 3, 'yz') ==> "bat"
rightPad('bat', 5, '') ==> "bat "
'abc def'.split() ==> [ "abc", "def" ]
'abc def'.split(' ') ==> [ "abc", "def" ]
' abc def '.split(?, ' ') ==> [ "abc", "def" ]
split('ab:cd:ef', ':') ==> [ "ab", "cd", "ef" ]
'ab:cd:ef'.splitMax(':', 0) ==> [ "ab", "cd", "ef" ]
splitMax('ab:cd:ef', ':', 2, true) ==> [ "ab", "cd:ef" ]
'ab:cd:ef'.separate(':') ==> [ "ab", "cd", "ef" ]
separate('ab-!-cd-!-ef', '-!-') ==> [ "ab", "cd", "ef" ]
'ab:cd:ef'.separateMax(':', 0) ==> [ "ab", "cd", "ef" ]
separateMax('ab-!-cd-!-ef', '-!-', 2, true) ==> [ "ab", "cd-!-ef" ]
' abc '.strip(' ') ==> "abc"
' abcyx'.strip('xyz') ==> " abc"
strip('z abcyx', 'xyz') ==> " abc"
' abc '.stripEnd(' ') ==> " abc"
'z abcyx'.stripEnd('xyz') ==> "z abc"
stripEnd('z abcyx', 'xyz') ==> "z abc"
' abc '.stripStart(' ') ==> "abc "
'z abcyx'.stripStart('xyz') ==> " abcyx"
stripStart('z abcyx', 'xyz') ==> " abcyx"
'abc'.substr(1) ==> "bc"
'abc'.substr(0, 2) ==> "ab"
'abc'.substr(?, 1, 2) ==> "b"
substr('abc', -2, -1) ==> "b"
2.substr('abc', -4, ?) ==> "ab"
'abc'.trim() ==> "abc"
trim(' abc ') ==> "abc"
'Cat'.uncapitalize() ==> "cat"
uncapitalize('CAt') ==> "cAt"
'Cat'.upperCase() ==> "CAT"
upperCase('cAt') ==> "CAT"
'i am a man .and..i have_a__pen'.camelCase() ==> "iAmAManAndIHaveAPen"
' Text to__c@mel case '.camelCase() ==> "textToC@melCase"
' Text to__c@mel case '.camelCase(' _.@') ==> "textToCMelCase"
'i am a man .and..i have_a__pen'.camelCase() ==> "IAmAManAndIHaveAPen"
' Text to__c@mel case '.camelCase() ==> "TextToC@melCase"
' Text to__c@mel case '.camelCase(' _.@') ==> "TextToCMelCase"
' Text to__snake case '.snakeCase() ==> "Text_to_snake_case"
'camelToSnakeCase'.snakeCase() ==> "camel_To_Snake_Case"
' Text to__snake case '.lowerSnakeCase() ==> "text_to_snake_case"
'camelToSnakeCase'.lowerSnakeCase() ==> "camel_to_snake_case"
' Text to__snake case '.upperSnakeCase() ==> "TEXT_TO_SNAKE_CASE"
'camelToSnakeCase'.upperSnakeCase() ==> "CAMEL_TO_SNAKE_CASE"
' Text to__snake case '.camelSnakeCase() ==> "Text_To_Snake_Case"
'camelToSnakeCase'.camelSnakeCase() ==> "Camel_To_Snake_Case"
'Peggy''s cat'.singleQuote() ==> "'Peggy''s cat'"
123.singleQuote() ==> "'123'"
quote('Raymond''s dog') ==> "'Raymond''s dog'"
q(True) ==> "'true'"
'Peggy\"s cat'.doubleQuote() ==> "\"Peggy\\\"s cat\""
12.3.doubleQuote() ==> "\"12.3\""
qq('Raymond\"s dog') ==> "\"Raymond\\\"s dog\""
qq(False) ==> "\"false\""
'2022-01-02T03:04:05'.amPmOfDay() ==> "AM"
amPmOfDay('2022-02-04T13:14:15') ==> "PM"
'2022-01-02T03:04:05'.second() ==> 5
second('2022-02-04T13:14:15') ==> 15
'2022-01-02T03:04:05'.secondOfDay() ==> 11045
secondOfDay('2022-02-04T13:14:15') ==> 47655
'2022-01-02T03:04:05'.minute() ==> 4
minute('2022-02-04T13:14:15') ==> 14
'2022-01-02T03:04:05'.minuteOfDay() ==> 184
minuteOfDay('2022-02-04T13:14:15') ==> 794
'2022-01-02T03:04:05'.hourOfAmPm() ==> 3
hourOfAmPm('2022-02-04T13:14:15') ==> 1
'2022-01-02T03:04:05'.hour() ==> 3
hour('2022-02-04T13:14:15') ==> 13
'2022-01-02T03:04:05'.dayOfWeek() ==> 7
dayOfWeek('2022-02-04T13:14:15') ==> 5
'2022-01-02T03:04:05'.day() ==> 2
day('2022-02-04T13:14:15') ==> 4
'2022-01-02T03:04:05'.dayOfYear() ==> 2
dayOfYear('2022-02-04T13:14:15') ==> 35
'2022-01-02T03:04:05'.month() ==> 1
month('2022-02-04T13:14:15') ==> 2
'2022-01-02T03:04:05'.year() ==> 2022
year('2022-02-04T13:14:15') ==> 2022
'2022-01-02T03:04:05'.plusSeconds(9) ==> "2022-01-02T03:04:14"
'2022-01-02T03:04:05'.plusSeconds(?, 10) ==> "2022-01-02T03:04:15"
plusSeconds('2022-02-04T13:14:15', 9) ==> "2022-02-04T13:14:24"
'2022-01-02T03:04:05'.plusMinutes(9) ==> "2022-01-02T03:13:05"
'2022-01-02T03:04:05'.plusMinutes(?, 10) ==> "2022-01-02T03:14:05"
plusMinutes('2022-02-04T13:14:15', 9) ==> "2022-02-04T13:23:15"
'2022-01-02T03:04:05'.plusHours(9) ==> "2022-01-02T12:04:05"
'2022-01-02T03:04:05'.plusHours(?, 10) ==> "2022-01-02T13:04:05"
plusHours('2022-02-04T13:14:15', 9) ==> "2022-02-04T22:14:15"
'2022-01-02T03:04:05'.plusDays(9) ==> "2022-01-11T03:04:05"
'2022-01-02T03:04:05'.plusDays(?, 10) ==> "2022-01-12T03:04:05"
plusDays('2022-02-04T13:14:15', 9) ==> "2022-02-13T13:14:15"
'2022-01-02T03:04:05'.plusWeeks(9) ==> "2022-03-06T03:04:05"
'2022-01-02T03:04:05'.plusWeeks(?, 10) ==> "2022-03-13T03:04:05"
plusWeeks('2022-02-04T13:14:15', 9) ==> "2022-04-08T13:14:15"
'2022-01-02T03:04:05'.plusMonths(9) ==> "2022-10-02T03:04:05"
'2022-01-02T03:04:05'.plusMonths(?, 10) ==> "2022-11-02T03:04:05"
plusMonths('2022-02-04T13:14:15', 9) ==> "2022-11-04T13:14:15"
'2022-01-02T03:04:05'.plusYears(9) ==> "2031-01-02T03:04:05"
'2022-01-02T03:04:05'.plusYears(?, 10) ==> "2032-01-02T03:04:05"
plusYears('2022-02-04T13:14:15', 9) ==> "2031-02-04T13:14:15"
'2022-01-02T03:04:05'.minusSeconds(9) ==> "2022-01-02T03:03:56"
'2022-01-02T03:04:05'.minusSeconds(?, 10) ==> "2022-01-02T03:03:55"
minusSeconds('2022-02-04T13:14:15', 9) ==> "2022-02-04T13:14:06"
'2022-01-02T03:04:05'.minusMinutes(9) ==> "2022-01-02T02:55:05"
'2022-01-02T03:04:05'.minusMinutes(?, 10) ==> "2022-01-02T02:54:05"
minusMinutes('2022-02-04T13:14:15', 9) ==> "2022-02-04T13:05:15"
'2022-01-02T03:04:05'.minusHours(9) ==> "2022-01-01T18:04:05"
'2022-01-02T03:04:05'.minusHours(?, 10) ==> "2022-01-01T17:04:05"
minusHours('2022-02-04T13:14:15', 9) ==> "2022-02-04T04:14:15"
'2022-01-02T03:04:05'.minusDays(9) ==> "2021-12-24T03:04:05"
'2022-01-02T03:04:05'.minusDays(?, 10) ==> "2021-12-23T03:04:05"
minusDays('2022-02-04T13:14:15', 9) ==> "2022-01-26T13:14:15"
'2022-01-02T03:04:05'.minusWeeks(9) ==> "2021-10-31T03:04:05"
'2022-01-02T03:04:05'.minusWeeks(?, 10) ==> "2021-10-24T03:04:05"
minusWeeks('2022-02-04T13:14:15', 9) ==> "2021-12-03T13:14:15"
'2022-01-02T03:04:05'.minusMonths(9) ==> "2021-04-02T03:04:05"
'2022-01-02T03:04:05'.minusMonths(?, 10) ==> "2021-03-02T03:04:05"
minusMonths('2022-02-04T13:14:15', 9) ==> "2021-05-04T13:14:15"
'2022-01-02T03:04:05'.minusYears(9) ==> "2013-01-02T03:04:05"
'2022-01-02T03:04:05'.minusYears(?, 10) ==> "2012-01-02T03:04:05"
minusYears('2022-02-04T13:14:15', 9) ==> "2013-02-04T13:14:15"
'2022-01-02T03:04:05.229390600'.truncateToMicro() ==> "2022-01-02T03:04:05.229390"
truncateToMicro('2022-02-04T13:14:15.229390600') ==> "2022-02-04T13:14:15.229390"
'2022-01-02T03:04:05.229390600'.truncateToMilli() ==> "2022-01-02T03:04:05.229"
truncateToMilli('2022-02-04T13:14:15.229390600') ==> "2022-02-04T13:14:15.229"
'2022-01-02T03:04:05.229390600'.truncateToSecond() ==> "2022-01-02T03:04:05"
truncateToSecond('2022-02-04T13:14:15.229390600') ==> "2022-02-04T13:14:15"
'2022-01-02T03:04:05.229390600'.truncateToMinute() ==> "2022-01-02T03:04"
truncateToMinute('2022-02-04T13:14:15.229390600') ==> "2022-02-04T13:14"
'2022-01-02T03:04:05.229390600'.truncateToHour() ==> "2022-01-02T03:00"
truncateToHour('2022-02-04T13:14:15.229390600') ==> "2022-02-04T13:00"
'2022-01-02T03:04:05.229390600'.truncateToDay() ==> "2022-01-02T00:00"
truncateToDay('2022-02-04T13:14:15.229390600') ==> "2022-02-04T00:00"
'2022-01-02T03:04:05.229390600'.truncateToMonth() ==> "2022-01-01T00:00"
truncateToMonth('2022-02-04T13:14:15.229390600') ==> "2022-02-01T00:00"
'2022-01-02T03:04:05.229390600'.truncateToYear() ==> "2022-01-01T00:00"
truncateToYear('2022-02-04T13:14:15.229390600') ==> "2022-01-01T00:00"
'2022-01-02T03:04'.withNano(789) ==> "2022-01-02T03:04:00.000000789"
'2022-01-02T03:04'.withNano(?, 789) ==> "2022-01-02T03:04:00.000000789"
withNano('2022-02-04T13:14', 789) ==> "2022-02-04T13:14:00.000000789"
'2022-01-02T03:04'.withMicro(789) ==> "2022-01-02T03:04:00.000789"
'2022-01-02T03:04'.withMicro(?, 789) ==> "2022-01-02T03:04:00.000789"
withMicro('2022-02-04T13:14', 789) ==> "2022-02-04T13:14:00.000789"
'2022-01-02T03:04'.withMilli(789) ==> "2022-01-02T03:04:00.789"
'2022-01-02T03:04'.withMilli(?, 789) ==> "2022-01-02T03:04:00.789"
withMilli('2022-02-04T13:14', 789) ==> "2022-02-04T13:14:00.789"
'2022-01-02T03:04'.withSecond(35) ==> "2022-01-02T03:04:35"
'2022-01-02T03:04'.withSecond(?, 35) ==> "2022-01-02T03:04:35"
withSecond('2022-02-04T13:14', 35) ==> "2022-02-04T13:14:35"
'2022-01-02T03:04'.withMinute(35) ==> "2022-01-02T03:35"
'2022-01-02T03:04'.withMinute(?, 35) ==> "2022-01-02T03:35"
withMinute('2022-02-04T13:14', 35) ==> "2022-02-04T13:35"
'2022-01-02T03:04'.withHour(16) ==> "2022-01-02T16:04"
'2022-01-02T03:04'.withHour(?, 16) ==> "2022-01-02T16:04"
withHour('2022-02-04T13:14', 16) ==> "2022-02-04T16:14"
'2022-01-02T03:04'.withDay(25) ==> "2022-01-25T03:04"
'2022-01-02T03:04'.withDay(?, 25) ==> "2022-01-25T03:04"
withDay('2022-02-04T13:14', 25) ==> "2022-02-25T13:14"
'2022-01-02T03:04'.withDayOfYear(123) ==> "2022-05-03T03:04"
'2022-01-02T03:04'.withDayOfYear(?, 123) ==> "2022-05-03T03:04"
withDayOfYear('2022-02-04T13:14', 123) ==> "2022-05-03T13:14"
'2022-01-02T03:04'.withMonth(7) ==> "2022-07-02T03:04"
'2022-01-02T03:04'.withMonth(?, 7) ==> "2022-07-02T03:04"
withMonth('2022-02-04T13:14', 7) ==> "2022-07-04T13:14"
'2022-01-02T03:04'.withYear(2047) ==> "2047-01-02T03:04"
'2022-01-02T03:04'.withYear(?, 2047) ==> "2047-01-02T03:04"
withYear('2022-02-04T13:14', 2047) ==> "2047-02-04T13:14"
'2022-01-02T03:04'.dayEnd() ==> "2022-01-02T23:59:59.999999999"
dayEnd('2022-02-04T13:14') ==> "2022-02-04T23:59:59.999999999"
'2022-01-02T03:04'.monthEnd() ==> "2022-01-31T23:59:59.999999999"
monthEnd('2022-02-04T13:14') ==> "2022-02-28T23:59:59.999999999"
'2022-01-02T03:04'.yearEnd() ==> "2022-12-31T23:59:59.999999999"
yearEnd('2022-02-04T13:14') ==> "2022-12-31T23:59:59.999999999"
'2022-01-02T03:04'.lengthOfMonth() ==> 31
lengthOfMonth('2022-02-04T13:14') ==> 28
'2022-01-02T03:04'.lengthOfYear() ==> 365
lengthOfYear('2024-02-04T13:14') ==> 366
'2020-01-02T23:04'.untilInSecond('2022-06-11T01:02') ==> 76903080
untilInSecond('2021-12-12T13:14','2021-03-03T01:00') ==> -24581640
'2020-01-02T23:04'.untilInMinute('2022-06-11T01:02') ==> 1281718
untilInMinute('2021-12-12T13:14','2021-03-03T01:00') ==> -409694
'2020-01-02T23:04'.untilInHour('2022-06-11T01:02') ==> 21361
untilInHour('2021-12-12T13:14','2021-03-03T01:00') ==> -6828
'2020-01-02T23:04'.untilInDay('2022-06-11T01:02') ==> 890
untilInDay('2021-12-12T13:14','2021-03-03T01:00') ==> -284
'2020-01-02T23:04'.untilInMonth('2022-06-11T01:02') ==> 29
untilInMonth('2021-12-12T13:14','2021-03-03T01:00') ==> -9
'2020-01-02T23:04'.untilInYear('2022-06-11T01:02') ==> 2
untilInYear('2021-12-12T13:14','2021-03-03T01:00') ==> 0
'2022-01-02T03:04:05'.localToOffsetDate() ==> "2022-01-02T03:04:05+08:00"
localToOffsetDate('2022-02-04T13:14:15') ==> "2022-02-04T13:14:15+08:00"
'2022-01-02T03:04:05+08:00'.offsetToLocalDate() ==> "2022-01-02T03:04:05"
offsetToLocalDate('2022-02-04T13:14:15+08:00') ==> "2022-02-04T13:14:15"
'2022-01-02T03:04:05.12345'.localDateToMillis() ==> 1641063845123
localDateToMillis('2022-02-04T13:14:15.12345') ==> 1643951655123
1641063845123.millisToLocalDate() ==> "2022-01-02T03:04:05.123"
millisToLocalDate(1643951655123) ==> "2022-02-04T13:14:15.123"
'2022-01-02T03:04:05.12345'.localDateToSeconds() ==> 1641063845
localDateToSeconds('2022-02-04T13:14:15.12345') ==> 1643951655
1641063845.secondsToLocalDate() ==> "2022-01-02T03:04:05"
secondsToLocalDate(1643951655) ==> "2022-02-04T13:14:15"
'2022-01-02T03:04:05.12345+08:00'.offsetDateToMillis() ==> 1641063845123
offsetDateToMillis('2022-02-04T13:14:15.12345+08:00') ==> 1643951655123
1641063845123.millisToOffsetDate() ==> "2022-01-02T03:04:05.123+08:00"
millisToOffsetDate(1643951655123) ==> "2022-02-04T13:14:15.123+08:00"
'2022-01-02T03:04:05.12345+08:00'.offsetDateToSeconds() ==> 1641063845
offsetDateToSeconds('2022-02-04T13:14:15.12345+08:00') ==> 1643951655
1641063845.secondsToOffsetDate() ==> "2022-01-02T03:04:05+08:00"
secondsToOffsetDate(1643951655) ==> "2022-02-04T13:14:15+08:00"
now() ==> "2022-10-30T23:52:47.084650900"
now().localToOffsetDate() ==> 2022-10-31T01:02:27.294741100+08:00s
'abcdefghijklmnopqrstuvwxyz~!@#$%^&*()_+-=ABCDEFGHIJKLMNOPQRSTUVWXYZ'.b64Encode()
==>
"YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXp+IUAjJCVeJiooKV8rLT1BQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWg=="
b64EncodeNoPadding('abcdefghijklmnopqrstuvwxyz~!@#$%^&*()_+-=ABCDEFGHIJKLMNOPQRSTUVWXYZ')
==>
"YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXp+IUAjJCVeJiooKV8rLT1BQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWg"
'abcdefghijklmnopqrstuvwxyz~!@#$%^&*()_+-=ABCDEFGHIJKLMNOPQRSTUVWXYZ'.b64MimeEncode()
==>
"YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXp+IUAjJCVeJiooKV8rLT1BQkNERUZHSElKS0xNTk9Q\r\nUVJTVFVWV1hZWg=="
b64MimeEncodeNoPadding('abcdefghijklmnopqrstuvwxyz~!@#$%^&*()_+-=ABCDEFGHIJKLMNOPQRSTUVWXYZ')
==>
"YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXp+IUAjJCVeJiooKV8rLT1BQkNERUZHSElKS0xNTk9Q\r\nUVJTVFVWV1hZWg"
'abcdefghijklmnopqrstuvwxyz~!@#$%^&*()_+-=ABCDEFGHIJKLMNOPQRSTUVWXYZ'.b64UrlEncode()
==>
"YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXp-IUAjJCVeJiooKV8rLT1BQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWg=="
b64UrlEncodeNoPadding('abcdefghijklmnopqrstuvwxyz~!@#$%^&*()_+-=ABCDEFGHIJKLMNOPQRSTUVWXYZ')
==>
"YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXp-IUAjJCVeJiooKV8rLT1BQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWg"
'YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXp+IUAjJCVeJiooKV8rLT1BQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWg=='.b64Decode()
==>
"abcdefghijklmnopqrstuvwxyz~!@#$%^&*()_+-=ABCDEFGHIJKLMNOPQRSTUVWXYZ"
b64Decode('YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXp+IUAjJCVeJiooKV8rLT1BQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWg')
==>
"abcdefghijklmnopqrstuvwxyz~!@#$%^&*()_+-=ABCDEFGHIJKLMNOPQRSTUVWXYZ"
'YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXp+IUAjJCVeJiooKV8rLT1BQkNERUZHSElKS0xNTk9Q\nUVJTVFVWV1hZWg=='.b64MimeDecode()
==>
"abcdefghijklmnopqrstuvwxyz~!@#$%^&*()_+-=ABCDEFGHIJKLMNOPQRSTUVWXYZ"
b64MimeDecode('YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXp+IUAjJCVeJiooKV8rLT1BQkNERUZHSElKS0xNTk9Q\r\nUVJTVFVWV1hZWg')
==>
"abcdefghijklmnopqrstuvwxyz~!@#$%^&*()_+-=ABCDEFGHIJKLMNOPQRSTUVWXYZ"
'YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXp-IUAjJCVeJiooKV8rLT1BQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWg=='.b64UrlDecode()
==>
"abcdefghijklmnopqrstuvwxyz~!@#$%^&*()_+-=ABCDEFGHIJKLMNOPQRSTUVWXYZ"
b64UrlDecode('YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXp-IUAjJCVeJiooKV8rLT1BQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWg')
==>
"abcdefghijklmnopqrstuvwxyz~!@#$%^&*()_+-=ABCDEFGHIJKLMNOPQRSTUVWXYZ"
'www.domain.com?a=1+2&b=3+4'.urlEncode() ==> "www.domain.com%3Fa%3D1%2B2%26b%3D3%2B4"
urlEncode('www.domain.com?a=1+2&b=3+4') ==> "www.domain.com%3Fa%3D1%2B2%26b%3D3%2B4"
'www.domain.com%3Fa%3D1%2B2%26b%3D3%2B4'.urlDecode() ==> "www.domain.com?a=1+2&b=3+4"
urlDecode('www.domain.com%3Fa%3D1%2B2%26b%3D3%2B4') ==> "www.domain.com?a=1+2&b=3+4"
'~!@#$%^&*()<>[]{}+-= "''\|_:;,./?'.escapeHtml() ==> "~!@#$%^&*()<>[]{}+-= "'\|_:;,./?"
'~!@#$%^&*()<>[]{}+-= "''\|_:;,./?'.unescapeHtml() ==> "~!@#$%^&*()<>[]{}+-= "'\|_:;,./?"
'~!@#$%^&*()<>[]{}+-= "''\|_:;,./?'.escapeXml() ==> "~!@#$%^&*()<>[]{}+-= "'\|_:;,./?"
'~!@#$%^&*()<>[]{}+-= "'\|_:;,./?'.unescapeXml() ==> "~!@#$%^&*()<>[]{}+-= "'\|_:;,./?"
json('{"a":1,"b":2,"c":3}').if(a.isEven(), 'T', 'F') ==> "F"
json('{"a":1,"b":2,"c":3}').if([a=1], 'T', 'F') ==> "T"
json('{"a":1,"b":2,"c":3}').if([a=1 & b=3], 'T', 'F') ==> "F"
json('{"a":1,"b":2,"c":3}').if([a=1 & b=3], 'T') ==> !unresolvable!
json('{"a":1,"b":2,"c":3}').if([a=b], 'T', if([c=3], 'C', 'F')) ==> "C"
json('[1,2,3,4,5]').if(isOdd(), calc(?*2), ?) ==> [ 2.0, 2, 6.0, 4, 10.0 ]
json('{"a":1,"b":2,"c":3}').if(a.isEven(), 'T', 'F') ==> "T"
json('{"a":1,"b":2,"c":3}').if([a=1 & b=3], 'T') ==> "T"
json('{"a":1,"b":2,"c":3}').if([a=b], 'T', if([c=3], 'C', 'F')) ==> "T"
json('[1,2,3,4,5]').if(isOdd(), calc(?*2), ?) ==> [ 1, 4.0, 3, 8.0, 5 ]
json('["abc","",123,false,null]').coalesce('xyz') ==> [ "abc", "", 123, false, "xyz" ]
json('{"a":null,"c":"abc"}').coalesce(a,b,c,'xyz') ==> "abc"
'a'.caseValue('A',1,'b',2,'a',3,4) ==> 3
'z'.caseValue('A',1,'b',2,'a',3,4) ==> 4
'z'.caseValue('A',1,'b',2,'a',3) ==> !unresolvable!
json('[{"s":1},{"s":null},{"s":3}]').s.caseValue(1,'A',null,'B') ==> [ "A", "B", null ]
'a'.caseValue('A',1,'b',2,'a',3,4) ==> 1
'z'.caseValue('A',1,'b',2,'a',3,4) ==> 4
'z'.caseValue('A',1,'b',2,'a',3) ==> !unresolvable!
0.cycleValue('a','b','c','d') ==> "a"
1.cycleValue(json('["a","b","c","d"]')) ==> "b"
'3'.cycleValue('a','b','c','d') ==> "d"
4.cycleValue('a','b','c','d') ==> "a"
-1.cycleValue('a','b','c','d') ==> "d"
-6.cycleValue('a','b','c','d') ==> "c"
json('{"a":1,"b":"B","c":null}').default(x) ==> ""
json('{"a":1,"b":"B","c":null}').default(x,'Hi') ==> "Hi"
json('{"a":1,"b":"B","c":null}').default(x,null,c,a,b) ==> 1
json('{"a":1,"b":"B","c":null}').default(x,null,c,b,a) ==> "B"
0.indexedValue('a','b','c','d') ==> "a"
1.indexedValue(json('["a","b","c","d"]')) ==> "b"
'3'.indexedValue('a','b','c','d') ==> "d"
4.indexedValue('a','b','c','d') ==> !unresolvable!
-1.indexedValue('a','b','c','d') ==> !unresolvable!
'2022-01-02T03:04:05'.formatDate('dd/MM/yyyy HH:mm:ss') ==> "02/01/2022 03:04:05"
'2022-01-02T03:04:05'.formatDate(?, 'yyyy-MM-dd') ==> "2022-01-02"
formatDate('2022-01-02T03:04:05', 'EEE, MMM d, yyyy') ==> "Sun, Jan 2, 2022"
12345.6.formatNumber('HK$#,##0.00') ==> "HK$12,345.60"
123.formatNumber(?, '#,##0.#') ==> "123"
formatNumber(123.45, '#,##0.#') ==> "123.5"
'Dog'.formatText('[%-5s]') ==> "[Dog ]"
123.formatText(?, '[%5d]') ==> "[ 123]"
formatText('Dog', '[%5s]') ==> "[ Dog]"
formatTexts('1:%s 2:%s 3:%s', 'a', 'b', 'c') ==> "1:a 2:b 3:c"
'b'.formatTexts('1:%s 2:%s 3:%s', 'a', ?, 'c') ==> "1:a 2:b 3:c"
json('{"A":"a","B":"b"}').formatTexts('1:%s 2:%s 3:%s', A, B, 'c') ==> "1:a 2:b 3:c"
json('[{"a":1,"b":3},{"a":2,"b":4}]').formatTexts('a=%d b=%d',a,b) ==> [ "a=1 b=3", "a=2 b=4" ]
'123'.toNumber() ==> 123.0
toNumber('abc') ==> 0.0
toNumber(true) ==> 1.0
toNumber(null) ==> !unresolvable!
toNumber(json('{"a":1}')) ==> !unresolvable!
toNumber(json('[1,2.0,"a",true,null]')) ==> [ 1, 2.0, 0.0, 1.0, null ]
123.toString() ==> "123"
toString(false) ==> "false"
toString(null) ==> "null"
toString(json('{"a":1}')) ==> "{\"a\":1}"
toString(json('[1,2.0,"a",true,null]')) ==> "[1,2.0,\"a\",true,null]"
123.toText() ==> "123"
toText(false) ==> "false"
toText(null) ==> "null"
toText(json('{"a":1}')) ==> !unresolvable!
toText(json('[1,2.0,"a",true,null]')) ==> [ "1", "2.0", "a", "true", "null" ]
json('{"len1":"12.3\"","len2":null,"len3":"64.0\""}').csv() ==> "\"12.3\"\"\",,\"64.0\"\"\""
csv(json('[[[[1,2],["3","4\""]]],{"a":1,"b":[2.0,8.888],"c":{"d":true,"e":null}}]')) ==> "1,2,3,\"4\"\"\",1,2.0,8.888,true,"
json('{"len1":"12.3\"","len2":null,"len3":"64.0\""}').csvShowNull() ==> "12.3\"\"\",null,\"64.0\"\""
csvShowNull(json('[[[[1,2],["3","4\""]]],{"a":1,"b":[2.0,8.888],"c":{"d":true,"e":null}}]')) ==> "1,2,3,\"4\"\"\",1,2.0,8.888,true,null"
json('{"len1":"12.3","len2":null,"len3":"64.0\""}').csvParams() ==> "'12.3',null,'64.0\"'"
csvParams(json('[[[[1,2],["3","4''"]]],{"a":1,"b":[2.0,8.888],"c":{"d":true,"e":null}}]')) ==> "1,2,'3','4''',1,2.0,8.888,true,null"
'abcde'.contains('bc') ==> true
contains('abcde','B') ==> false
json('[1.0,2.8,3.0]').contains(?, '1') ==> false
json('[1.0,2.8,3.0]').contains(1) ==> true
contains(json('["1","2","3"]'), 2.0) ==> true
contains(json('[1.0,2.8,3.00]'), '3.0') ==> true
json('["1.0","2.0","3.0"]').contains(?, '3.0') ==> true
json('[1,2,null,4]').contains(null) ==> true
json('{"a":1,"b":2,"c":3}').contains('a') ==> true
'abcde'.containsIgnoreCase('bc') ==> true
containsIgnoreCase('abcde','B') ==> true
json('["a","b","c"]').containsIgnoreCase(?, 'B') ==> true
containsIgnoreCase(json('["a","b","c"]'), 'bc') ==> false
json('{"a":1,"b":2,"c":3}').containsIgnoreCase('A') ==> true
'abcde'.notContains('bc') ==> false
notContains('abcde','B') ==> true
json('[1.0,2.8,3.0]').notContains(?, 1) ==> false
json('[1,2,null,4]').notContains(null) ==> false
'abcde'.notContainsIgnoreCase('bc') ==> false
notContainsIgnoreCase('abcde','B') ==> false
json('["a","b","c"]').notContainsIgnoreCase(?, 'D') ==> true
'abcdef'.startsWith('abc') ==> true
'ABCDEF'.startsWith(?,'abc') ==> false
startsWith('ABCDEF','cde') ==> false
'abcdef'.startsWithIgnoreCase('abc') ==> true
'ABCDEF'.startsWithIgnoreCase(?,'abc') ==> true
startsWithIgnoreCase('ABCDEF','cde') ==> false
'abcdef'.notStartsWith('abc') ==> false
'ABCDEF'.notStartsWith(?,'abc') ==> true
notStartsWith('ABCDEF','cde') ==> true
'abcdef'.notStartsWithIgnoreCase('abc') ==> false
'ABCDEF'.notStartsWithIgnoreCase(?,'abc') ==> false
notStartsWithIgnoreCase('ABCDEF','cde') ==> true
'abcdef'.endsWith('def') ==> true
'ABCDEF'.endsWith(?,'def') ==> false
endsWith('ABCDEF','cde') ==> false
'abcdef'.endsWithIgnoreCase('def') ==> true
'ABCDEF'.endsWithIgnoreCase(?,'def') ==> true
endsWithIgnoreCase('ABCDEF','cde') ==> false
'abcdef'.notEndsWith('def') ==> false
'ABCDEF'.notEndsWith(?,'def') ==> true
notEndsWith('ABCDEF','cde') ==> true
'abcdef'.notEndsWithIgnoreCase('def') ==> false
'ABCDEF'.notEndsWithIgnoreCase(?,'def') ==> false
notEndsWithIgnoreCase('ABCDEF','cde') ==> true
'abc'.equals('abc') ==> true
'abc'.equals(?,' abc') ==> false
equals('ABC','abc') ==> false
'abc'.equalsIgnoreCase('abc') ==> true
'abc'.equalsIgnoreCase(?,' abc') ==> false
equalsIgnoreCase('ABC','abc') ==> true
'abc'.notEquals('abc') ==> false
'abc'.notEquals(?, ' abc') ==> true
notEquals('ABC','abc') ==> true
'abc'.notEqualsIgnoreCase('abcd') ==> true
'abc'.notEqualsIgnoreCase(' abc') ==> true
notEqualsIgnoreCase('ABC','abc') ==> false
'123a'.matches('^[0-9]+$') ==> false
'784238'.matches(?,'^[0-9]+$') ==> true
matches('63 56','^[0-9]+$') ==> false
'1234-123456'.notMatches('\\d{4}-\\d{6}') ==> false
'888-123456'.notMatches(?,'\\d{4}-\\d{6}') ==> true
notMatches('4444-5555','\\d{4}-\\d{6}') ==> true
56.in(12,34,56) ==> true
'56'.in(12,34,56) ==> true
'A'.in(json('["a","b","c"]')) ==> false
'A'.inIgnoreCase('a','b','c') ==> true
'a '.inIgnoreCase('a','b','c') ==> false
56.notIn(12,34,56) ==> false
'56'.notIn(12,34,56) ==> false
'A'.notIn(json('["a","b","c"]')) ==> true
'A'.notInIgnoreCase('a','b','c') ==> false
'a '.notInIgnoreCase('a','b','c') ==> true
''.isEmpty() ==> true
isEmpty(' ') ==> false
isEmpty(1) ==> false
isEmpty(true) ==> false
isEmpty(null) ==> true
isEmpty(json('[""," ",0,false,null]')) ==> [ true, false, false, false, true ]
''.isNotEmpty() ==> false
isNotEmpty(' ') ==> true
isNotEmpty(1) ==> true
isNotEmpty(true) ==> true
isNotEmpty(null) ==> false
isNotEmpty(json('[""," ",0,false,null]')) ==> [ false, true, true, true, false ]
''.isBlank() ==> true
isBlank(' ') ==> true
isBlank(json('[""," ","X",0,false,null]')) ==> [ true, true, false, false, false, false ]
''.isNotBlank() ==> false
isNotBlank(' ') ==> false
isNotBlank(json('[""," ","X",0,false,null]')) ==> [ false, false, true, false, false, false ]
null.isNull() ==> true
isNull(null) ==> true
isNull('') ==> false
isNull(json('["text",1,true,null]')) ==> [ false, false, false, true ]
null.isNotNull() ==> false
isNotNull(null) ==> false
isNotNull('') ==> true
isNotNull(json('["text",1,true,null]')) ==> [ true, true, true, false ]
'text'.isText() ==> true
isText(1) ==> false
isText(true) ==> false
isText(json('["text",1,true,null]')) ==> [ true, false, false, false ]
'text'.isBoolean() ==> false
isBoolean(1) ==> false
isBoolean(true) ==> true
isBoolean(json('["text",1,true,null]')) ==> [ false, false, true, false ]
'text'.isNumber() ==> false
isNumber(1) ==> true
isNumber(true) ==> false
isNumber(json('["text",1,true,null]')) ==> [ false, true, false, false ]
1.isEven() ==> false
isEven(2) ==> true
isEven(json('["text",1,2,null]')) ==> [ false, false, true, false ]
1.isOdd() ==> true
isOdd(2) ==> false
isOdd(json('["text",1,2,null]')) ==> [ false, true, false, false ]
'text'.isArray() ==> false
isArray(1) ==> false
isArray(null) ==> false
json('[1,2]').isArray() ==> true
isArray(json('{"a":1}')) ==> false
'text'.isObject() ==> false
isObject(1) ==> false
isObject(null) ==> false
json('[1,2]').isObject() ==> false
isObject(json('{"a":1}')) ==> true
json('[]').isEmptyArray() ==> true
isEmptyArray(json('[0]')) ==> false
json('{}').isEmptyObject() ==> true
isEmptyObject(json('{"a":1}')) ==> false
true.not() ==> false
not(false) ==> true
not('false') ==> false
not(0) ==> false
not(null) ==> false
'2021-12-31T00:00:00'.isWeekday() ==> true
isWeekday('2022-01-01T00:00:00') ==> false
'2021-12-31T00:00:00'.isWeekend() ==> false
isWeekend('2022-01-01T00:00:00') ==> true
'2020-12-31T00:00:00'.isLeapYear() ==> true
isLeapYear('2022-01-01T00:00:00') ==> false
json('[7,1,9,null,5,3]').size() ==> 6
size(json('[7,1,9,null,5,3]')) ==> 6
json('[7,1,9,null,5,3]').lastIndex() ==> 5
lastIndex(json('[7,1,9,null,5,3]')) ==> 5
json('[1,1,3,5,null,3,7,3,9]').indexOf(3) ==> 2
json('[1,1,3,5,null,3,7,3,9]').indexOf(?, '1') ==> 0
indexOf(json('[1,1,3,5,null,3,7,3,9]'), null) ==> 4
json('[1,1,3,5,null,3,7,3,9]').lastIndexOf(3) ==> 7
json('[1,1,3,5,null,3,7,3,9]').lastIndexOf(?, '1') ==> 1
lastIndexOf(json('[1,1,3,5,null,3,7,3,9]'), null) ==> 4
json('[7,1,9,null,5,3]').first() ==> 7
first(json('[null,7,1,9,5,3]')) ==> null
json('[7,1,9,null,5,3]').last() ==> 3
last(json('[7,1,9,5,3,null]')) ==> null
json('[7,1,9,null,5,3]').max() ==> 9
max(json('[7,1,9,null,5,3]'), 15, 16) ==> 16
json('[7,1,9,null,5,3]').min() ==> 1
min(json('[7,1,9,null,5,3]'), 15, 16) ==> 1
json('[7,1,9,null,5,3]').topN(2) ==> [ 9, 7 ]
topN(json('[7,1,9,null,5,3]'), 6) ==> [ 9, 7, 5, 3, 1 ]
json('[7,1,9,null,5,3]').bottomN(2) ==> [ 1, 3 ]
bottomN(json('[7,1,9,null,5,3]'), 6) ==> [ 1, 3, 5, 7, 9 ]
json('[7,1,9,null,5,3]').sum() ==> 25.0
sum(json('[7,1,9,null,5,3]'), 15, 16) ==> 56.0
json('[7,1,9,null,5,3]').avg() ==> 5.0
avg(json('[7,1,9,null,5,3]'), 15, 16) ==> 8.0
json('[7,1,9,null,5,3]').count() ==> 5
count(json('[7,1,9,null,5,3]'), 15, 16) ==> 7
json('[7,1,9,null,5,3]').push(10,'X',json('[-1,-2]'),json('{"a":11,"b":12}'))
==>
[ 7, 1, 9, null, 5, 3, 10, "X", [ -1, -2 ], {
"a" : 11,
"b" : 12
} ]
json('[7,1,9,null,5,3]').reverse() ==> [ 3, 5, null, 9, 1, 7 ]
reverse(json('[7,1,9,null,5,3]')) ==> [ 3, 5, null, 9, 1, 7 ]
json('[1,2,3,4,5,6,7,8,9]').slice(3) ==> [ 4, 5, 6, 7, 8, 9 ]
json('[1,2,3,4,5,6,7,8,9]').slice(2,8) ==> [ 3, 4, 5, 6, 7, 8 ]
json('[1,2,3,4,5,6,7,8,9]').slice(,5) ==> [ 1, 2, 3, 4, 5 ]
json('[1,2,3,4,5,6,7,8,9]').slice(-5) ==> [ 5, 6, 7, 8, 9 ]
json('[1,2,3,4,5,6,7,8,9]').slice(?,1,8,2) ==> [ 2, 4, 6, 8 ]
json('[1,2,3,4,5,6,7,8,9]').slice(?,2,,2) ==> [ 3, 5, 7, 9 ]
slice(json('[1,2,3,4,5,6,7,8,9]'),6,2,1) ==> [ 7, 6, 5, 4 ]
slice(json('[1,2,3,4,5,6,7,8,9]'),,,3) ==> [ 1, 4, 7 ]
slice(json('[1,2,3,4,5,6,7,8,9]'),,-5,1) ==> [ 1, 2, 3, 4 ]
json('[1,1,3,5,3,7,3,9]').sort() ==> [ 1, 1, 3, 3, 3, 5, 7, 9 ]
json('[1,1,3,5,3,7,3,9]').sort(?,-1) ==> [ 9, 7, 5, 3, 3, 3, 1, 1 ]
json('[{"seq":4,"val":"A"},{"seq":1,"val":"B"},{"seq":3,"val":"C"},{"seq":2,"val":"D"}]').sort(seq)
==>
[ {
"seq" : 1,
"val" : "B"
}, {
"seq" : 2,
"val" : "D"
}, {
"seq" : 3,
"val" : "C"
}, {
"seq" : 4,
"val" : "A"
} ]
json('[{"seq":4,"val":"A"},{"seq":1,"val":"B"},{"seq":3,"val":"C"},{"seq":2,"val":"D"}]').sort(seq,-1)
==>
[ {
"seq" : 4,
"val" : "A"
}, {
"seq" : 3,
"val" : "C"
}, {
"seq" : 2,
"val" : "D"
}, {
"seq" : 1,
"val" : "B"
} ]
json('[1,1,3,5,3,7,3,9]').distinct().sort() ==> [ 1.0, 3.0, 5.0, 7.0, 9.0 ]
distinct(json('["A","Z","a","Z","A","z"]')) ==> [ "A", "a", "Z", "z" ]
distinct(json('["1","1.0",1,1.0,1.00,true,"true",null,"null"]')) ==> [ "1", "1.0", "null", "true", 1.0, true ]
json('["Hello", ",", "World", "!"]').join() ==> "Hello,World!"
json('[1,2,3]').join('+') ==> "1+2+3"
join(json('["A",1,"B","2.00","C",3.00,"D",true,null]'),'/') ==> "A/1/B/2.00/C/3.0/D/true"
json('[{"code":"A","price":8},{"code":"B","price":8},{"code":"C","price":3}]').findAndModify([code='C'],field(price:99))
==>
[ {
"code" : "A",
"price" : 8
}, {
"code" : "B",
"price" : 8
}, {
"code" : "C",
"price" : 99
} ]
json('[{"code":"A","price":8},{"code":"B","price":8},{"code":"C","price":3}]').findAndModify([price=8],field(price:99),2)
==>
[ {
"code" : "A",
"price" : 99
}, {
"code" : "B",
"price" : 99
}, {
"code" : "C",
"price" : 3
} ]
json('[{"code":"A","price":8},{"code":"B"},{"code":"C","price":3},{"code":"D","price":8},{"code":"E","price":5}]').findByMax(price)
==>
{
"code" : "A",
"price" : 8
}
findByMax(json('[{"code":"A","price":8},{"code":"B"},{"code":"C","price":3},{"code":"D","price":8},{"code":"E","price":5}]'), code)
==>
{
"code" : "E",
"price" : 5
}
json('[{"code":"A","price":8},{"code":"B"},{"code":"C","price":3},{"code":"D","price":8},{"code":"E","price":5}]').findByMin(?,price)
==>
{
"code" : "C",
"price" : 3
}
findByMin(json('[{"code":"A","price":8},{"code":"B"},{"code":"C","price":3},{"code":"D","price":8},{"code":"E","price":5}]'), code)
==>
{
"code" : "A",
"price" : 8
}
json('[{"code":"A","price":8},{"code":"B"},{"code":"C","price":3},{"code":"D","price":8},{"code":"E","price":5}]').findByNullOrMax(price)
==>
{
"code" : "B"
}
findByNullOrMax(json('[{"code":"A","price":8},{"code":"B"},{"code":"C","price":3},{"code":"D","price":8},{"code":"E","price":5}]'), code)
==>
{
"code" : "E",
"price" : 5
}
json('[{"code":"A","price":8},{"code":"B"},{"code":"C","price":3},{"code":"D","price":8},{"code":"E","price":5}]').findByNullOrMin(?,price)
==>
{
"code" : "B"
}
findByNullOrMin(json('[{"code":"A","price":8},{"code":"B"},{"code":"C","price":3},{"code":"D","price":8},{"code":"E","price":5}]'), code)
==>
{
"code" : "A",
"price" : 8
}
json('[{"code":"A","price":8},{"code":"B"},{"code":"C","price":3},{"code":"D","price":8},{"code":"E","price":5}]').findByMaxOrNull(price)
==>
{
"code" : "A",
"price" : 8
}
findByMaxOrNull(json('[{"code":"A","price":8},{"code":"B"},{"code":"C","price":3},{"code":"D","price":8},{"code":"E","price":5}]'), code)
==>
{
"code" : "E",
"price" : 5
}
json('[{"code":"A","price":8},{"code":"B"},{"code":"C","price":3},{"code":"D","price":8},{"code":"E","price":5}]').findByMinOrNull(?,price)
==>
{
"code" : "C",
"price" : 3
}
findByMinOrNull(json('[{"code":"A","price":8},{"code":"B"},{"code":"C","price":3},{"code":"D","price":8},{"code":"E","price":5}]'), code)
==>
{
"code" : "A",
"price" : 8
}
json('[1,"2",{"a":1,"b":2}]')
==>
[ 1, "2", {
"a" : 1,
"b" : 2
} ]
'{"a":1,"b":[2,3],"c":{"d":4,"e":5}}'.json()
==>
{
"a" : 1,
"b" : [ 2, 3 ],
"c" : {
"d" : 4,
"e" : 5
}
}
json('{"a":1,"b":2}').let($x:a, $y:calc(a+b), $z:concat(a,b)).map($x,$y,$z)
==>
{
"$x" : 1,
"$y" : 3.0,
"$z" : "12"
}
json('{"decode":[{"code":"A","color":"Red"},{"code":"B","color":"Blue"}],"data":["B","A","B"]}')
[email protected]($code:?).get(...decode[code=$code].color)
==>
[ "Blue", "Red", "Blue" ]
json('{"a":1,"b":[2,3],"c":{"d":4,"e":5}}').entries()
==>
[ {
"key" : "a",
"value" : 1
}, {
"key" : "b",
"value" : [ 2, 3 ]
}, {
"key" : "c",
"value" : {
"d" : 4,
"e" : 5
}
} ]
json('{"a":1,"b":[2,3],"c":{"d":4,"e":5}}').keys() ==> [ "a", "b", "c" ]
json('{"a":1,"b":[2,3],"c":{"d":4,"e":5}}').keys(2) ==> [ "a", "b", "c", "d", "e" ]
keys(json('{"a":1,"b":[2,3],"c":{"d":4,"e":5}}'), -1) ==> [ "a", "b", "c", "d", "e" ]
json('{"id":1,"array":[{"id":2,"obj":{"id":3,"array":[{"id":4.1},{"id":4.2}]}}]}').depthLimit(1)
==>
{
"id" : 1
}
json('{"id":1,"array":[{"id":2,"obj":{"id":3,"array":[{"id":4.1},{"id":4.2}]}}]}').depthLimit(2)
==>
{
"id" : 1,
"array" : [ {
"id" : 2
} ]
}
depthLimit(json('{"id":1,"array":[{"id":2,"obj":{"id":3,"array":[{"id":4.1},{"id":4.2}]}}]}'),3)
==>
{
"id" : 1,
"array" : [ {
"id" : 2,
"obj" : {
"id" : 3
}
} ]
}
depthLimit(json('{"id":1,"array":[{"id":2,"obj":{"id":3,"array":[{"id":4.1},{"id":4.2}]}}]}'),4)
==>
{
"id" : 1,
"array" : [ {
"id" : 2,
"obj" : {
"id" : 3,
"array" : [ {
"id" : 4.1
}, {
"id" : 4.2
} ]
}
} ]
}
'Hi'.collect(1,?,true,json('[{"a":1,"x":11},{"b":2,"y":12}]'),json('{"c":3,"x":13}'))
==>
[ 1, "Hi", true, [ {
"a" : 1,
"x" : 11
}, {
"b" : 2,
"y" : 12
} ], {
"c" : 3,
"x" : 13
} ]
json('{"id":1,"val":11,"item":{"id":2,"val":22,"item":{"id":3,"val":33,"item":{"id":4,"val":44}}}}')
.cumulateCollect(map(id,val.calc(?*2)), item)
==>
[ {
"id" : 1,
"val" : 22.0
}, {
"id" : 2,
"val" : 44.0
}, {
"id" : 3,
"val" : 66.0
}, {
"id" : 4,
"val" : 88.0
} ]
json('["Hi"]').wrap() ==> [ [ "Hi" ] ]
wrap(json('{"a":1}'))
==>
[ {
"a" : 1
} ]
json('{"a":1,"b":[2,3],"c":{"d":4,"e":5}}').toArray()
==>
[ 1, [ 2, 3 ], {
"d" : 4,
"e" : 5
} ]
json('{"a":1,"b":[2,3],"c":{"d":4,"e":5}}').toArray(c) ==> [ 4, 5 ]
toArray(json('{"a":1,"b":[2,3],"c":{"d":4,"e":5}}').toArray()) ==> [ 1, 2, 3, 4, 5 ]
'a'.toObject('text')
==>
{
"text" : "a"
}
99.toObject('number')
==>
{
"number" : 99
}
json('[1,2,3]').toObject('array')
==>
{
"array" : [ 1, 2, 3 ]
}
json('{"a":1,"b":2}').toObject('obj')
==>
{
"obj" : {
"a" : 1,
"b" : 2
}
}
json('[[1,2],[3,4],[5,6]]').mergeArrays(?) ==> [ 1, 2, 3, 4, 5, 6 ]
json('[{"a":[1,2]},{"a":[3,4]},{"a":[5,6]}]').mergeArrays(a) ==> [ 1, 2, 3, 4, 5, 6 ]
json('{"a":[1,2],"b":[3,4],"c":[5,6]}').mergeArrays(a,b,c) ==> [ 1, 2, 3, 4, 5, 6 ]
json('[{"a":1,"x":11},{"b":2,"y":12},{"c":3,"x":13}]').mergeObjects()
==>
{
"a" : 1,
"x" : 13,
"b" : 2,
"y" : 12,
"c" : 3
}
mergeObjects(json('[{"a":1,"x":11},{"b":2,"y":12}]'), json('{"c":3,"x":13}'))
==>
{
"a" : 1,
"x" : 13,
"b" : 2,
"y" : 12,
"c" : 3
}
json('[[[[1,2],[3,4]],[[5,6],[7,8]]],[[[9,10],[11,12]],[[13,14],[15,16]]]]').flatten(1)
==>
[ [ [ 1, 2 ], [ 3, 4 ] ], [ [ 5, 6 ], [ 7, 8 ] ], [ [ 9, 10 ], [ 11, 12 ] ], [ [ 13, 14 ], [ 15, 16 ] ] ]
json('[[[[1,2],[3,4]],[[5,6],[7,8]]],[[[9,10],[11,12]],[[13,14],[15,16]]]]').flatten(2)
==>
[ [ 1, 2 ], [ 3, 4 ], [ 5, 6 ], [ 7, 8 ], [ 9, 10 ], [ 11, 12 ], [ 13, 14 ], [ 15, 16 ] ]
json('[[[[1,2],[3,4]],[[5,6],[7,8]]],[[[9,10],[11,12]],[[13,14],[15,16]]]]').flatten()
==>
[ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 ]
flatten(json('[[[[1,2],[3,4]],[[5,6],[7,8]]],[[[9,10],[11,12]],[[13,14],[15,16]]]]'), 3, null)
==>
[ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 ]
flatten(json('{"a":1,"b":[2,3],"c":{"d":4,"e":{"f":5}}}'), '_', null)
==>
{
"a" : 1,
"b_0" : 2,
"b_1" : 3,
"c_d" : 4,
"c_e_f" : 5
}
json('[0,1,[2,3,[4,{"a":5},6,[7]],8],9]').flatten('_')
==>
{
"0" : 0,
"1" : 1,
"2_0" : 2,
"2_1" : 3,
"2_2_0" : 4,
"2_2_1_a" : 5,
"2_2_2" : 6,
"2_2_3_0" : 7,
"2_3" : 8,
"3" : 9
}
flatten(json('{"a":1,"b":[2,3],"c":{"d":4,"e":{"f":5}}}'), '.', '[%d]')
==>
{
"a" : 1,
"b[0]" : 2,
"b[1]" : 3,
"c.d" : 4,
"c.e.f" : 5
}
json('[0,1,[2,3,[4,{"a":5},6,[7]],8],9]').flatten('.', '[%d]')
==>
{
"[0]" : 0,
"[1]" : 1,
"[2][0]" : 2,
"[2][1]" : 3,
"[2][2][0]" : 4,
"[2][2][1].a" : 5,
"[2][2][2]" : 6,
"[2][2][3][0]" : 7,
"[2][3]" : 8,
"[3]" : 9
}
flatten(json('{"a":1,"b":[2,3],"c":{"d":4,"e":{"f":5}}}'),'_',null).unflatten('_')
==>
{
"a" : 1,
"b" : [ 2, 3 ],
"c" : {
"d" : 4,
"e" : {
"f" : 5
}
}
}
json('[0,1,[2,3,[4,{"a":5},6,[7]],8],9]').flatten('_').unflatten('_')
==>
[ 0, 1, [ 2, 3, [ 4, {"a":5}, 6, [ 7 ] ], 8 ], 9 ]
flatten(json('{"a":1,"b":[2,3],"c":{"d":4,"e":{"f":5}}}'),'.','[%d]').unflatten('.[]')
==>
{
"a" : 1,
"b" : [ 2, 3 ],
"c" : {
"d" : 4,
"e" : {
"f" : 5
}
}
}
json('[0,1,[2,3,[4,{"a":5},6,[7]],8],9]').flatten('.','[%d]').unflatten('.[]')
==>
[ 0, 1, [ 2, 3, [ 4, {"a":5}, 6, [ 7 ] ], 8 ], 9 ]
json('{"a":1,"b":[2,3],"c":{"d":4,"e":5}}').map(c.e,c.d,b,a)
==>
{
"e" : 5,
"d" : 4,
"b" : [ 2, 3 ],
"a" : 1
}
json('{"a":1,"b":[2,3],"c":{"d":4,"e":5}}').map(cc:c.map(dd:d,ee:e),xx:map(aa:a,bb:b))
==>
{
"cc" : {
"dd" : 4,
"ee" : 5
},
"xx" : {
"aa" : 1,
"bb" : [ 2, 3 ]
}
}
json('{"a":1,"b":[2,3],"c":{"d":4,"e":5}}').field(f:6,c:)
==>
{
"a" : 1,
"b" : [ 2, 3 ],
"f" : 6
}
json('{"id":"1782-734828-A","name":"Cyron"}').field(id.split('-')@.repeat('X',length()).@join('-'))
==>
{
"id" : "XXXX-XXXXXX-X",
"name" : "Cyron"
}
json('[{"a":1,"b":"A"},{"a":2,"b":"B"},{"a":3,"b":"C"},{"a":2,"b":"D"},{"a":1,"b":"E"}]').group(a)
==>
[ {
"a" : 1,
"elements" : [ {
"a" : 1,
"b" : "A"
}, {
"a" : 1,
"b" : "E"
} ]
}, {
"a" : 2,
"elements" : [ {
"a" : 2,
"b" : "B"
}, {
"a" : 2,
"b" : "D"
} ]
}, {
"a" : 3,
"elements" : [ {
"a" : 3,
"b" : "C"
} ]
} ]
json('[{"a":1,"b":"A"},{"a":2,"b":"B"},{"a":3,"b":"C"},{"a":2,"b":"D"},{"a":1,"b":"E"}]').group(a,bs:b)
==>
[ {
"a" : 1,
"bs" : [ "A", "E" ]
}, {
"a" : 2,
"bs" : [ "B", "D" ]
}, {
"a" : 3,
"bs" : [ "C" ]
} ]
json('[{"a":1,"bs":["A","E"]},{"a":2,"bs":["B","D"]},{"a":3,"bs":["C"]}]').unwind(b:bs)
==>
[ {
"a" : 1,
"b" : "A"
}, {
"a" : 1,
"b" : "E"
}, {
"a" : 2,
"b" : "B"
}, {
"a" : 2,
"b" : "D"
}, {
"a" : 3,
"b" : "C"
} ]
json('{"xy1": 1,"xy2": 2,"ab1": 3,"ab2": 4,"ab3": 5,"zz1": 6,"xy3": 7,"zz2": 9,"zz3": {"k":10}}}').assort(*.[isEven()], ~'xy.*', ~'ab.*', ??)
==>
[ {
"xy2" : 2,
"ab2" : 4,
"zz1" : 6
}, {
"xy1" : 1,
"xy3" : 7
}, {
"ab1" : 3,
"ab3" : 5
}, {
"zz2" : 9
}, {
"zz3" : {
"k" : 10
}
} ]
json('[1,2,3,4,5,6,7,8,9,10,11,12]').assort([?<5], [isEven()], [?<9], ?)
==>
[ [ 1, 2, 3, 4 ], [ 6, 8, 10, 12 ], [ 5, 7 ], [ 9, 11 ] ]
json('{"a":{"b":{"c":1}}}').a.steps() ==> 2
json('{"a":{"b":{"c":1}}}').a.b.steps() ==> 3
json('{"a":{"b":{"c":1}}}').*().c.steps() ==> 4
json('{"a":{"b":{"c":1}}}').a.b.calc(c+1).steps() ==> 4
json('{"a":{"b":{"c":1}}}').a.b.c.calc(?+1).steps() ==> 5
Jossons stores JSON datasets in a map of type Map<String, Josson>
for placeholder resolution.
To create a Jossons object without data.
Jossons jossons = new Jossons();
To create a Jossons object with given Jackson ObjectNode. Each entry under root of the ObjectNode will become a member of the default dataset mapping.
Jossons jossons = Jossons.create(jsonNode);
To create a Jossons object with given JSON string that deserialized to a Jackson ObjectNode. Each entry under root of the ObjectNode will become a member of the default dataset mapping.
Jossons jossons = Jossons.fromJsonString("{...}");
To create a Jossons object with given text-based dataset mapping Map<String, String>
.
Jossons jossons = Jossons.fromMap(mapping);
To create a Jossons object with given integer-based dataset mapping Map<String, Integer>
.
Jossons jossons = Jossons.fromMapOfInt(mapping);
To add more default dataset entry to a Jossons object afterward.
jossons.putDataset("key", josson);
To tell Jossons what markup language escaping operation is required.
Default is MarkupLanguage.NONE
for plain text.
jossons.escapingMarkup(MarkupLanguage.XML);
jossons.escapingMarkup(MarkupLanguage.HTML);
A placeholder is enclosed by double curly braces. A template is a text based document layout with Jossons placeholders. It can be any format, such as plain text, HTML or XML.
To present a dataset entry's value content as text.
{{key}}
To apply a Josson Query on a dataset entry's value.
{{key->query}}
Placeholders can be nested and are resolved from inside to outside. Resolved placeholder is replaced with text and continue for the next round.
Example
|<----------------------------------------------- 3 --------------------------------->|
| |<---------------------------- 2 -------------------------->| |
| | |<------ 1 ------>| | |
| | | | | |
{{stock->[itemCode={{order->items[qrCode={{qrCode->quote()}}].itemCode.quote()}}].qty}}
{{qrCode->quote()}}
is resolved to'1234567890'
{{order->items[qrCode='1234567890'].itemCode.quote()}}
is resolved to'ABCDE'
{{stock->[itemCode='ABCDE'].qty}}
is resolved to100
The resolved text value is allowed to contain {
and }
.
Any combination of these symbols in the text will not influence the next round of template placeholder resolution.
This mechanism can prevent injection attack.
The ternary pattern can be repeated with no limit.
{{boolean ? trueValue : falseValue}}
{{boolean ? trueValue : boolean ? trueValue : falseValue}}
{{boolean ? trueValue ⟬: boolean ? trueValue⟭ⁿ : falseValue}}
If all conditions are evaluated to be false and falseValue
is not given, it returns an empty string.
{{boolean ? trueValue}}
{{boolean ? trueValue : boolean ? trueValue}}
{{boolean ? trueValue ⟬: boolean ? trueValue⟭ⁿ }}
Syntax ?:
is much like coalesce()
but the checking conditions of valueAsText
are unresolvable and empty string in addition to null node.
{{valueAsText ?: valueAsText}}
{{valueAsText ⟬?: valueAsText⟭ⁿ }}
If valueAsText
is unresolvable, valueAsText?
returns an empty string instead of throws NoValuePresentException
.
The following two statements have the same result.
{{valueAsText?}}
{{valueAsText ?: ''}}
The above syntax can be mixed in a placeholder. For example:
{{boolean ? trueValue : anotherValue ?: anotherAnotherValue?}}
Josson query works on single JSON dataset. In order to let a placeholder output to include data from two datasets. It is required to use join operation to build a new dataset for the placeholder.
At least one matching key must be given and the number of key on both side must be the same.
Join operations match keyL1
with keyR1
, keyL2
with keyR2
and so on.
There are two join types for left and right join.
- Join One - Find the first matched object node and merge the object elements.
- Join Many - Find all matched nodes and embed inside the object as a new array node.
For Join Many operations, the arrayName:
is optional.
If arrayName
is not given, the last element name of the query is used.
-
Inner Join
>=<
"leftQuery{keyL1,keyL2...} >=< rightQuery{keyR1,keyR2...}"
-
Left Join One
<=<
"leftQuery{keyL1,keyL2...} <=< rightQuery{keyR1,keyR2...}"
-
Right Join One
>=>
"leftQuery{keyL1,keyL2...} >=> rightQuery{keyR1,keyR2...}"
-
Left Join Many
<=<<
"leftQuery{keyL1,keyL2...} <=<< rightQuery{arrayName:keyR1,keyR2...}"
-
Right Join Many
>>=>
"leftQuery{arrayName:keyL1,keyL2...} >>=> rightQuery{keyR1,keyR2...}"
-
Left Excluding Join
<!<
"leftQuery{keyL1,keyL2...} <!< rightQuery{keyR1,keyR2...}"
-
Right Excluding Join
>!>
"leftQuery{keyL1,keyL2...} >!> rightQuery{keyR1,keyR2...}"
-
Outer Excluding Join
<!>
"leftQuery{keyL1,keyL2...} <!> rightQuery{keyR1,keyR2...}"
Set operations do not need matching key.
-
Left Concatenate
<+<
Concatenate right into left. Works on two objects or two arrays. If one is object and the other is array, the object will be put into an array before manipulation.
"leftQuery <+< rightQuery"
-
Right Concatenate
>+>
Concatenate left into right. Works on two objects or two arrays. If one is object and the other is array, the object will be put into an array before manipulation.
"leftQuery >+> rightQuery"
-
Subtract Right From Left
<-<
Set of elements in the left set that are not in the right set. Works on two objects or two arrays. If one is object and the other is array, the object will be put into an array before manipulation.
"leftQuery <-< rightQuery"
-
Subtract Left From Right
>->
Set of elements in the right set that are not in the left set. Works on two objects or two arrays. If one is object and the other is array, the object will be put into an array before manipulation.
"leftQuery >-> rightQuery"
-
Symmetric Difference
<->
Set of elements in either set but not in the intersection. Works on two objects or two arrays.
"leftQuery <-> rightQuery"
-
Union
<u>
Set of all elements in the collection of sets. Works on two arrays.
"leftQuery <u> rightQuery"
-
Intersection
>n<
Set of elements that exists in both set. Works on two arrays.
"leftQuery >n< rightQuery"
The chaining Pipe is used to perform multiple join and set operations within a single expression.
This chaining operation will be chained using the pipe operator |
.
... | {keyL1,keyL2...} <JoinOperator> query{keyR1,keyR2...} | ...
... | <SetOperator> query | ...
Example
"queryA{aKey1} >=> queryB{bKey1} | <+< queryC | {cKey1,cKey2} <=<< queryD{arrayName:dKey1,dKey2}"
Key $
returns a BooleanNode
with true
value.
Key $now
returns a TextNode
of now with date and time. e.g. 2022-01-01T19:34:47.787144100
Key $today
returns a TextNode
of today's date. e.g. 2022-01-01T00:00
Key $yesterday
returns a TextNode
of yesterday's date. e.g. 2021-12-31T00:00
Key $tomorrow
returns a TextNode
of tomorrow's date. e.g. 2022-01-02T00:00
Key $params
returns an ArrayNode
of a Dictionary Function's parameters in an array.
Key $0
, $1
, $2
... returns a JsonNode
of a Dictionary Function's individual parameter naming in zero-based index.
Below is the JSON for this tutorial. The created Jossons object's dataset map has two entries where the keys are "order" and "company".
{
"order": {
"salesOrderId": "SO0001",
"salesDate": "2022-01-01T10:01:23",
"salesPerson": "Raymond",
"customer": {
"customerId": "CU0001",
"name": "Peggy",
"phone": "+852 62000610"
},
"items": [
{
"itemCode": "B00001",
"name": "WinWin TShirt Series A - 2022",
"brand": "WinWin",
"property": {
"size": "M",
"colors": [
"WHITE",
"RED"
]
},
"qty": 2,
"unit": "Pcs",
"unitPrice": 15.0,
"tags": [
"SHIRT",
"WOMEN"
]
},
{
"itemCode": "A00308",
"name": "OctoPlus Tennis Racket - Star",
"brand": "OctoPlus",
"property": {
"colors": [
"BLACK"
]
},
"qty": 1,
"unit": "Pcs",
"unitPrice": 150.0,
"unitDiscount": 10.0,
"tags": [
"TENNIS",
"SPORT",
"RACKET"
]
},
{
"itemCode": "A00201",
"name": "WinWin Sport Shoe - Super",
"brand": "WinWin",
"property": {
"size": "35",
"colors": [
"RED"
]
},
"qty": 1,
"unit": "Pair",
"unitPrice": 110.0,
"unitDiscount": 10.0,
"tags": [
"SHOE",
"SPORT",
"WOMEN"
]
}
],
"totalAmount": 270.0,
"discountPct": 5.0,
"netAmount": 256.5,
"delivery": {
"handlingFee": 5.0,
"address": "Wo Mun Street,\nFanling, N.T.,\nHong Kong",
"contactPerson": "Cyron",
"phone": "+852 26004198"
}
},
"company": {
"name": "Octomix Limited",
"phone": "+852 12345678",
"website": "www.octomix.com",
"address": [
"888 Queen's Road East",
"Hong Kong"
]
}
}
Function fillInPlaceholder()
uses the stored dataset mapping to merge and fill all placeholders in a template.
Any unresolvable placeholder will raise NoValuePresentException
with the incomplete merged text content.
All unresolvable placeholders are quoted with **
to replace the original double curly braces.
Jossons jossons = Jossons.fromJsonString(orderJsonString);
String output = jossons.fillInPlaceholder(template);
Template
"{{company->name.rightPad(65)}}INVOICE\n\n" +
"{{company->address[0].rightPad(56) ?: $->repeat(' ',56)}}Issue Date: {{order->salesDate.formatDate('dd/MM/yyyy')}}\n" +
"{{company->address[1].rightPad(58) ?: $->repeat(' ',58)}}Invoice#: {{order->salesOrderId.center(10)}}\n" +
"Phone: {{company->phone.rightPad(48)}}Customer ID: {{order->customer.customerId.center(10)}}\n" +
"Website: {{company->website.rightPad(49)}}Due Date: {{order->salesDate.plusMonths(1).formatDate('dd/MM/yyyy')}}\n\n" +
"BILL TO {{order->delivery!=null ? 'SHIP TO'}}\n" +
"{{order->customer.name.rightPad(30)}} {{order->delivery!=null ? order->coalesce(delivery.contactPerson,customer.name)}}\n" +
"{{order->customer.coalesce(phone,'N/A').concat('Phone: ',?).rightPad(30)}} " +
"{{order->delivery!=null ? order->coalesce(delivery.phone,customer.phone,'N/A').concat('Phone: ',?)}}\n" +
"{{order->delivery.address!=null ? order->delivery.address.split('\n').concat(repeat(' ',31),?).join('\n').concat(?,'\n')}}\n" +
"Item# Description Quantity Unit Price Discount Total\n" +
"----- ----------------------------------- -------- ---------- -------- --------\n" +
"{{order->items.concat(" +
" ##.center(5),' '," +
" name.rightPad(35),' '," +
" concat(qty,' ',unit).center(8),' '," +
" unitPrice.formatNumber('#,##0.0').leftPad(9),' '," +
" coalesce(unitDiscount,0).formatNumber('#,##0.0').leftPad(8),' '," +
" calc(qty * (unitPrice-d), d:coalesce(unitDiscount,0)).formatNumber('#,##0.0').leftPad(9)," +
" '\n ',itemCode,' '," +
" property.entries().concat(key,':',value.toString()).join(' ')" +
" ).join('\n')" +
"}}\n" +
"----- ----------------------------------- -------- ---------- -------- --------\n" +
"{{order->totalAmount.formatNumber('US$#,##0.0').leftPad(12).concat('Subtotal:',?,'\n').leftPad(80)}}" +
"{{order->discountPct > 0 ? order->discountPct.formatNumber('0.0').leftPad(11).concat('Discount:',?,'%\n').leftPad(80)}}" +
"{{order->delivery.handlingFee!=null ? order->delivery.handlingFee.formatNumber('US$#,##0.0').leftPad(12).concat('Shipping and handling:',?,'\n').leftPad(80)}}" +
"{{order->calc(netAmount+fee, fee:coalesce(delivery.handlingFee,0)).formatNumber('US$#,##0.0').leftPad(12).concat('Total:',?,'\n').leftPad(80)}}"
-
If
company->address[0]
is unresolvable, 56 spaces are printed.{{company->address[0].rightPad(56) ?: $->repeat(' ',56)}}
-
Due date is calculated from one month after the
salesDate
.{{order->salesDate.plusMonths(1).formatDate('dd/MM/yyyy')}}
-
"SHIP TO" is not printed if
order->delivery
does not exists.{{order->delivery!=null ? 'SHIP TO'}}
-
Delivery contact person is printed only if
order->delivery
is defined. Iforder->delivery.contactPerson
does not exists,order->customer.name
is printed instead.{{order->delivery!=null ? order->coalesce(delivery.contactPerson,customer.name)}}
-
If
order->delivery.address
exists, split it with delimiter\n
. Then add 31 spaces in front of each line and join them together with\n
. At last, add an extra\n
at the end.{{order->delivery.address!=null ? order->delivery.address.split('\n').concat(repeat(' ',31),?).join('\n').concat(?,'\n')}}
Path chart
order → delivery{} → address → split(?) → [""] → [concat(?) ⇒ ""] → join(?[]) ⇒ ""
-
Construct two lines for each item. Each item amount is calculated from
qty
,unitPrice
andunitDiscount
.{{ order->items.concat( ##.center(5), ' ', name.rightPad(35), ' ', concat(qty,' ',unit).center(8), ' ', unitPrice.formatNumber('#,##0.0').leftPad(9), ' ', coalesce(unitDiscount,0).formatNumber('#,##0.0').leftPad(8), ' ', calc(qty * (unitPrice-d), d:coalesce(unitDiscount,0)).formatNumber('#,##0.0').leftPad(9), '\n ', itemCode, ' ', property.entries().concat(key,':',value.toString()).join(' ') ).join('\n') }}
Path chart
order → items[]* → [{}] → [concat(%) ⇒ ""] → join(?[]) ⇒ ""
-
If
order->discountPct
is not > 0, the discount line is not printed.{{order->discountPct > 0 ? order->discountPct.formatNumber('0.0').leftPad(11).concat('Discount:',?,'%\n').leftPad(80)}}
-
Order total amount is calculated by adding
netAmount
anddelivery.handlingFee
.{{order->calc(netAmount+fee, fee:coalesce(delivery.handlingFee,0)).formatNumber('US$#,##0.0').leftPad(12).concat('Total:',?,'\n').leftPad(80)}}
Output
Octomix Limited INVOICE
888 Queen's Road East Issue Date: 01/01/2022
Hong Kong Invoice#: SO0001
Phone: +852 12345678 Customer ID: CU0001
Website: www.octomix.com Due Date: 01/02/2022
BILL TO SHIP TO
Peggy Cyron
Phone: +852 62000610 Phone: +852 26004198
32 Wo Mun Street,
Fanling, N.T.,
Hong Kong
Item# Description Quantity Unit Price Discount Total
----- ----------------------------------- -------- ---------- -------- --------
1 WinWin TShirt Series A - 2022 2 Pcs 15.0 0.0 30.0
B00001 size:M colors:["WHITE","RED"]
2 OctoPlus Tennis Racket - Star 1 Pcs 150.0 10.0 140.0
A00308 colors:["BLACK"]
3 WinWin Sport Shoe - Super 1 Pair 110.0 10.0 100.0
A00201 size:35 colors:["RED"]
----- ----------------------------------- -------- ---------- -------- --------
Subtotal: US$270.0
Discount: 5.0%
Shipping and handling: US$5.0
Total: US$261.5
Function fillInPlaceholderWithResolver()
uses the stored dataset mapping and with the help of on demand callback
dataset resolver to merge and fill all placeholders in a template.
String output = jossons.fillInPlaceholderWithResolver(template, dictionaryFinder, dataFinder, progress);
The last parameter progress
is a ResolverProgress
which record all the resolution progress steps.
By default, the last step "End" is added automatically.
The resolution process has three debug levels:
ResolverDebugLevel.SHOW_CONTENT_OF_VALUE_NODE_ONLY
(default)ResolverDebugLevel.SHOW_CONTENT_UP_TO_OBJECT_NODE
ResolverDebugLevel.SHOW_CONTENT_UP_TO_ARRAY_NODE
Basic constructors and methods:
ResolverProgress progress = new ResolverProgress();
ResolverProgress progress = new ResolverProgress("subject");
progress.debugLevel(ResolverDebugLevel.SHOW_CONTENT_UP_TO_OBJECT_NODE);
progress.autoMarkEnd(false);
List<String> steps = progress.getSteps();
If a key cannot be found in the default dataset mapping during the placeholder resolution process,
the resolver will ask Function<String, String> dictionaryFinder
for an answer.
dictionaryFinder
takes an argument String key
and returns a resolution statement of either:
-
A statement that represent a value.
"1" // IntNode "2.3" // DoubleNode "'Text'" // TextNode "true" // BooleanNode "null" // NullNode
-
Parse a JSON string of an object or array directly.
"{\"B00001\":2, \"A00308\":1, \"A00201\":1}" // ObjectNode "[{\"B00001\":2}, {\"A00308\":1}, {\"A00201\":1}]" // ArrayNode
-
A Jossons query that retrieve data from other dataset.
"otherKey->jossonQuery"
-
A Jossons query with ternary syntax, please refer to Ternary Syntax.
"statement ? otherKey1->jossonQuery : otherKey2->jossonQuery"
-
A join operation to merge two datasets, please refer to Join Operation.
"leftQuery{keyL1,keyL2...} <=< rightQuery{keyR1,keyR2...}"
-
A set operation on two datasets, please refer to Set Operation.
"leftQuery <u> rightQuery"
-
A database query statement, please refer to Data Finder.
"collectionName ? {findStatement}"
Resolved result will be cached with the key name except for key name starts with $
.
Next time a placeholder or statement query for the same key will return the cached value without evaluation.
After Dictionary Finder returned a valid database query statement,
resolver will further trigger BiFunction<String, String, Josson> dataFinder
callback.
dataFinder
takes two arguments String collectionName
and String query
, and returns a Josson object.
One-document query syntax that request for an ObjectNode
:
"collectionName ? {findStatement}"
"collectionName ? [aggregateStatements]"
"? {findStatement}"
"? [aggregateStatements]"
Many-documents query syntax that request for an ArrayNode
:
"collectionName[] ? {findStatement}"
"collectionName[] ? [aggregateStatements]"
"[] ? {findStatement}"
"[] ? [aggregateStatements]"
collectionName
is optional. If not given, the resolving key will be passed to dataFinder
in the collection name argument.
For Many-documents query request, the collection name argument has a suffix of []
.
Appendix has an example of MongoDB adapter for this Data Finder.
If a dictionaryFinder
key ends with ()
, then it is a dictionary function.
It's resolution statement can contain the following implicit variables.
$params
the calling statement's parameters in an array.$0
,$1
,$2
... the calling statement's individual parameter naming in zero-based index.
If it is necessary to pass the values in an array node as the function parameters in the form of (elem0, elem1, elem2...), use placeholder to transform by Josson function csvParams().
"customFunction({{arrayNode->csvParams()}})"
Dictionary finder entries
"double()" : "$0->calc(?*2)"
"sum2num()" : "$->calc({{$0}} + {{$1}})"
"sum2numThenDouble()" : "double(sum2num({{$0}},{{$1}}))->formatText('({{$0}}+{{$1}})x2 = %.1f')"
"projectName()" : "$0='CHI' ? '早晨' : 'Josson'"
"titledList()" : "$params->slice(1).concat(##,'. ',?).join('\n').concat({{$0->quote()}},'\n',{{$0->repeat('=',length()).quote()}},'\n',?)"
Placeholders
{{double(3)}} ==> "6.0"
{{sum2num(4,5)}} ==> "9.0"
{{sum2numThenDouble(1,2)}} ==> "(1+2)x2 = 6.0"
{{projectName()}} ==> "Josson"
{{projectName('CHI')}} ==> "早晨"
{{projectName('ENG')}} ==> "Josson"
{{titledList('List Title','Item A','Item B','Item C')}}
==>
List Title
==========
1. Item A
2. Item B
3. Item C
Map<String, String> dictionaryFinder = new HashMap<>();
dictionaryFinder.put("stocks", "[]?{ignoredQuery}");
dictionaryFinder.put("withStock", "order->items.map(itemCode,qty){itemCode} <=< stocks{itemCode}");
BiFunction<String, String, Josson> dataFinder = (collectionName, ignoredQuery) -> {
try {
if (collectionName.equals("stocks[]")) {
// Hardcode instead of database query
return Josson.fromJsonString("[" +
"{\"itemCode\":\"A00201\",\"onhandQty\":18}," +
"{\"itemCode\":\"A00308\",\"onhandQty\":76}," +
"{\"itemCode\":\"A00543\",\"onhandQty\":5}," +
"{\"itemCode\":\"B00001\",\"onhandQty\":231}," +
"{\"itemCode\":\"B00002\",\"onhandQty\":0}]");
}
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return null;
};
ResolverProgress progress = new ResolverProgress();
// Use the JSON data from section "Fill in"
Jossons jossons = Jossons.fromJsonString(orderJsonString);
String output = jossons.fillInPlaceholderWithResolver(
"Order ID : {{order->salesOrderId}}\n" +
"{{withStock->concat(itemCode.rightPad(10), 'Qty: ', qty, ' Onhand: ', onhandQty).join('\n')}}",
dictionaryFinder::get, dataFinder, progress));
Output
Order ID : SO0001
B00001 Qty: 2 Onhand: 231
A00308 Qty: 1 Onhand: 76
A00201 Qty: 1 Onhand: 18
Progress Steps
Round 1 : Resolving withStock from order->items.map(itemCode,qty){itemCode} <=< stocks{itemCode}
Round 2 : Matched stocks to data query []?{ignoredQuery}
Round 2 : Resolved stocks to Array with 5 elements
Round 2 : Resolved withStock = Array with 3 elements
Round 3 : End
Customize a BSON to JSON converter.
import org.bson.Document;
import org.bson.json.Converter;
import org.bson.json.JsonMode;
import org.bson.json.JsonWriterSettings;
import org.bson.json.StrictJsonWriter;
import org.bson.types.ObjectId;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.stream.Collectors;
public class Converters {
private static class ObjectIdConverter implements Converter<ObjectId> {
public static final ObjectIdConverter INSTANCE = new ObjectIdConverter();
@Override
public void convert(ObjectId value, StrictJsonWriter writer) {
writer.writeString(value.toHexString());
}
}
private static class EpochToLocalDateTimeConverter implements Converter<Long> {
public static final EpochToLocalDateTimeConverter INSTANCE = new EpochToLocalDateTimeConverter();
@Override
public void convert(Long value, StrictJsonWriter writer) {
LocalDateTime date = LocalDateTime.ofInstant(Instant.ofEpochMilli(value), ZoneId.of("Asia/Hong_Kong"));
writer.writeString(date.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
}
}
private static final JsonWriterSettings JSON_WRITER_SETTINGS = JsonWriterSettings
.builder()
.outputMode(JsonMode.RELAXED)
.objectIdConverter(ObjectIdConverter.INSTANCE)
.dateTimeConverter(EpochToLocalDateTimeConverter.INSTANCE)
.build();
public static String bsonToJson(Document bson) {
return bson == null ? null : bson.toJson(JSON_WRITER_SETTINGS);
}
public static String bsonListToJson(List<Document> bsonList) {
return bsonList == null || bsonList.isEmpty() ? null :
"[" + bsonList.stream()
.map(Converters::bsonToJson)
.collect(Collectors.joining(",")) +
"]";
}
}
Define dataFinder()
. Use MongoTemplate
to query MongoDB directly.
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.octomix.josson.Josson;
import org.bson.BsonDocument;
import org.bson.BsonValue;
import org.bson.Document;
import org.bson.codecs.BsonArrayCodec;
import org.bson.codecs.DecoderContext;
import org.bson.json.JsonReader;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.BasicQuery;
import org.springframework.stereotype.Repository;
import java.util.ArrayList;
import java.util.List;
import java.util.function.BiFunction;
import java.util.stream.Collectors;
@Repository
public class JsonDao {
@Autowired
private MongoTemplate mongoTemplate;
public List<Document> findDocuments(String collectionName, String query) {
return mongoTemplate.find(
new BasicQuery(query),
Document.class,
collectionName
);
}
public List<Document> aggregateDocuments(String collectionName, String operations) {
List<BsonDocument> pipeline = new BsonArrayCodec()
.decode(new JsonReader(operations), DecoderContext.builder().build())
.stream()
.map(BsonValue::asDocument)
.collect(Collectors.toList());
return mongoTemplate.getDb().getCollection(collectionName).aggregate(pipeline).into(new ArrayList<>());
}
public String findJsonString(String collectionName, String query) {
return bsonToJson(mongoTemplate.findOne(
new BasicQuery(query),
Document.class,
collectionName
));
}
public String aggregateJsonString(String collectionName, String operations) {
List<Document> documents = aggregateDocuments(collectionName, operations);
return documents.isEmpty() ? null : bsonToJson(documents.get(0));
}
public BiFunction<String, String, Josson> dataFinder() {
return (collectionName, query) -> {
if (collectionName.endsWith("[]")) {
collectionName = collectionName.substring(0, collectionName.length()-2);
List<Document> documents = query.charAt(0) == '['
? aggregateDocuments(collectionName, query)
: findDocuments(collectionName, query);
if (!documents.isEmpty()) {
ArrayNode array = Josson.createArrayNode();
documents.stream()
.map(Converters::bsonToJson)
.forEach(json -> {
try {
array.add(Josson.readJsonNode(json));
} catch (JsonProcessingException e) {
e.printStackTrace();
}
});
return Josson.create(array);
}
} else {
String json = query.charAt(0) == '['
? aggregateJsonString(collectionName, query)
: findJsonString(collectionName, query);
if (json != null) {
try {
return Josson.fromJsonString(json);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
}
}
return null;
};
}
}
(55934043)
[{
"file": [{
"fileRefNo": "AG/CSD/1",
"status": "Active"
}],
"requestNo": "225V49"
}, {
"file": [{
"fileRefNo": "AG/CSD/1",
"status": "Inactive"
}],
"requestNo": "225SRV"
}, {
"file": [{
"fileRefNo": "AG/CSD/2",
"status": "Active"
}],
"requestNo": "225SRV"
}]
Josson Query
group(requestNo, file).field(file.flatten(1))
Output
[ {
"requestNo" : "225V49",
"file" : [ {
"fileRefNo" : "AG/CSD/1",
"status" : "Active"
} ]
}, {
"requestNo" : "225SRV",
"file" : [ {
"fileRefNo" : "AG/CSD/1",
"status" : "Inactive"
}, {
"fileRefNo" : "AG/CSD/2",
"status" : "Active"
} ]
} ]
(73362526)
{
"success": 1,
"results": [
{
"FI": "120986750",
"event_id": "5164306",
"cards": {
"updated_at": "1660559432",
"key": "AAA100",
"sp": {
"cards": {
"id": "1",
"name": "Cards",
"odds": [
{
"id": "101",
"odds": "2.200",
"header": "Over",
"name": "11"
},
{
"id": "102",
"odds": "8.500",
"header": "Exactly",
"name": "11"
},
{
"id": "103",
"odds": "1.909",
"header": "Under",
"name": "11"
}
]
}
}
},
"corners": {
"updated_at": "1660559431",
"key": "AAA200",
"sp": {
"corners": {
"id": "2",
"name": "Corners",
"odds": [
{
"id": "201",
"odds": "2.200",
"header": "Over",
"name": "10"
},
{
"id": "202",
"odds": "8.500",
"header": "Exactly",
"name": "10"
},
{
"id": "203",
"odds": "1.909",
"header": "Under",
"name": "10"
}
]
},
"total_corners": {
"id": "3",
"name": "Total Corners",
"odds": [
{
"id": "204",
"odds": "17.000",
"name": "Under 6"
},
{
"id": "205",
"odds": "4.333",
"name": "6 - 8"
},
{
"id": "206",
"odds": "2.750",
"name": "9 - 11"
},
{
"id": "207",
"odds": "3.400",
"name": "12 - 14"
},
{
"id": "208",
"odds": "5.500",
"name": "Over 14"
}
]
}
}
}
}
]
}
Josson Query
results.map(
FI,
event_id,
categories: entries().[value.isObject()]*
.map(category: key,
value.updated_at,
value.key,
details: value.sp.**
.map(market_id: id,
market: name,
odds)
.unwind(odds)
).unwind(details)
).unwind(categories)
Output
[ {
"FI" : "120986750",
"event_id" : "5164306",
"category" : "cards",
"updated_at" : "1660559432",
"key" : "AAA100",
"market_id" : "1",
"market" : "Cards",
"id" : "101",
"odds" : "2.200",
"header" : "Over",
"name" : "11"
}, {
"FI" : "120986750",
"event_id" : "5164306",
"category" : "cards",
"updated_at" : "1660559432",
"key" : "AAA100",
"market_id" : "1",
"market" : "Cards",
"id" : "102",
"odds" : "8.500",
"header" : "Exactly",
"name" : "11"
}, {
"FI" : "120986750",
"event_id" : "5164306",
"category" : "cards",
"updated_at" : "1660559432",
"key" : "AAA100",
"market_id" : "1",
"market" : "Cards",
"id" : "103",
"odds" : "1.909",
"header" : "Under",
"name" : "11"
}, {
"FI" : "120986750",
"event_id" : "5164306",
"category" : "corners",
"updated_at" : "1660559431",
"key" : "AAA200",
"market_id" : "2",
"market" : "Corners",
"id" : "201",
"odds" : "2.200",
"header" : "Over",
"name" : "10"
}, {
"FI" : "120986750",
"event_id" : "5164306",
"category" : "corners",
"updated_at" : "1660559431",
"key" : "AAA200",
"market_id" : "2",
"market" : "Corners",
"id" : "202",
"odds" : "8.500",
"header" : "Exactly",
"name" : "10"
}, {
"FI" : "120986750",
"event_id" : "5164306",
"category" : "corners",
"updated_at" : "1660559431",
"key" : "AAA200",
"market_id" : "2",
"market" : "Corners",
"id" : "203",
"odds" : "1.909",
"header" : "Under",
"name" : "10"
}, {
"FI" : "120986750",
"event_id" : "5164306",
"category" : "corners",
"updated_at" : "1660559431",
"key" : "AAA200",
"market_id" : "3",
"market" : "Total Corners",
"id" : "204",
"odds" : "17.000",
"name" : "Under 6"
}, {
"FI" : "120986750",
"event_id" : "5164306",
"category" : "corners",
"updated_at" : "1660559431",
"key" : "AAA200",
"market_id" : "3",
"market" : "Total Corners",
"id" : "205",
"odds" : "4.333",
"name" : "6 - 8"
}, {
"FI" : "120986750",
"event_id" : "5164306",
"category" : "corners",
"updated_at" : "1660559431",
"key" : "AAA200",
"market_id" : "3",
"market" : "Total Corners",
"id" : "206",
"odds" : "2.750",
"name" : "9 - 11"
}, {
"FI" : "120986750",
"event_id" : "5164306",
"category" : "corners",
"updated_at" : "1660559431",
"key" : "AAA200",
"market_id" : "3",
"market" : "Total Corners",
"id" : "207",
"odds" : "3.400",
"name" : "12 - 14"
}, {
"FI" : "120986750",
"event_id" : "5164306",
"category" : "corners",
"updated_at" : "1660559431",
"key" : "AAA200",
"market_id" : "3",
"market" : "Total Corners",
"id" : "208",
"odds" : "5.500",
"name" : "Over 14"
} ]
(73025233)
Dataset api1
{
"name": "name1",
"address": "",
"skillset": [
{
"lang": "java",
"projectName": "project1"
},
{
"lang": "c++",
"projectName": "project2"
}
]
}
Dataset api2
{
"name": "name1",
"address": "",
"skillset": [
{
"lang": "c++",
"projectName": "project2"
},
{
"lang": "java",
"projectName": "project1"
}
]
}
Jossons Query
api1 = api2
Output
true
(73674131)
{
"idno":6473853,
"user":"GCA_GB",
"operation":"U",
"timestamp":"2022-08-22T13:14:48",
"first_name":{
"old":"rak",
"new":"raki"
},
"fam_name":{
"old":"gow",
"new":"gowda"
}
}
Josson Query
map(idno, user, operation, timestamp,
changes: entries().[value.isObject()]*.map(key::value))
.unwind(changes)
Output
[ {
"idno" : 6473853,
"user" : "GCA_GB",
"operation" : "U",
"timestamp" : "2022-08-22T13:14:48",
"first_name" : {
"old" : "rak",
"new" : "raki"
}
}, {
"idno" : 6473853,
"user" : "GCA_GB",
"operation" : "U",
"timestamp" : "2022-08-22T13:14:48",
"fam_name" : {
"old" : "gow",
"new" : "gowda"
}
} ]
(73456238)
{
"test": "t",
"testInt": 1,
"b": {
"testDouble": 1.1,
"c": [{
"testFloat": 1.2
}]
}
}
Josson Query
flatten('_')
Output flattened
{
"test" : "t",
"testInt" : 1,
"b_testDouble" : 1.1,
"b_c_0_testFloat" : 1.2
}
Josson Query
unflatten('_')
Output unflatten
{
"test" : "t",
"testInt" : 1,
"b" : {
"testDouble" : 1.1,
"c" : [ {
"testFloat" : 1.2
} ]
}
}
(73598200)
{
"id": 11,
"item": [
{
"id": "11_1",
"action": "add",
"type": {
"id": "11_1_xx",
"typeName": "xx"
},
"item": [
{
"id": "11_1_1",
"action": "add",
"type": {
"id": "11_1_1_zz",
"typeName": "zz"
},
"item": [
{
"id": "11_1_1_1",
"action": "add",
"type": {
"id": "11_1_1_1_xx",
"typeName": "xx"
}
}
]
},
{
"id": "11_1_2",
"action": "add",
"type": {
"id": "11_1_2_xx",
"typeName": "xx"
},
"item": [
{
"id": "11_1_2_1",
"action": "add",
"type": {
"id": "11_1_2_1_zz",
"typeName": "zz"
}
}
]
}
]
}
]
}
Josson Query
cumulateCollect(item[type.typeName='xx']*.field(item:), item).flatten(1)
Output
[ {
"id" : "11_1",
"action" : "add",
"type" : {
"id" : "11_1_xx",
"typeName" : "xx"
}
}, {
"id" : "11_1_2",
"action" : "add",
"type" : {
"id" : "11_1_2_xx",
"typeName" : "xx"
}
}, {
"id" : "11_1_1_1",
"action" : "add",
"type" : {
"id" : "11_1_1_1_xx",
"typeName" : "xx"
}
} ]
(72426983)
{
"userId": "1",
"age": "20",
"firstName": "firstname1",
"lastname": "lastname1",
"zipCode": "zipcode1",
"street": "street1",
"city": "city1",
"country": "country",
"gender": "gender1",
"grade": "grade1",
"birthday": "birthday1"
}
Josson Query
map(ID:userId, age, firstName, lastname,
address:collect(
map(code:'custom-field1',value:zipCode),
map(code:'custom-field2',value:street),
map(code:'custom-field3',value:city),
map(code:'custom-field4',value:country)),
gender, grade, birthday)
Output
{
"ID" : "1",
"age" : "20",
"firstName" : "firstname1",
"lastname" : "lastname1",
"address" : [ {
"code" : "custom-field1",
"value" : "zipcode1"
}, {
"code" : "custom-field2",
"value" : "street1"
}, {
"code" : "custom-field3",
"value" : "city1"
}, {
"code" : "custom-field4",
"value" : "country"
} ],
"gender" : "gender1",
"grade" : "grade1",
"birthday" : "birthday1"
}
(72701610)
{
"treasure": [
{
"aname": "FOO",
"bname": "BAR"
}
]
}
Josson Query
treasure
.entries()
.map(if([key='aname'], if([value='FOO'],'fname',key), 'sname')::value)
.mergeObjects()
Output
{
"fname" : "FOO",
"sname" : "BAR"
}
(73945727)
{
"status": [
{
"id": "online",
"state": "valid"
},
{
"id": "busy",
"state": "unknown"
},
{
"id": "any",
"state": "unknow",
"moreInfo": "unavailable"
}
],
"users": [
{
"title": "foo",
"availability": [
"online",
"busy"
]
},
{
"title": "bar",
"availability": [
"busy",
"any"
]
},
{
"title": "baz",
"availability": [
"any"
]
}
]
}
Josson Query
map(users.field([email protected]($id:?).get($.status[id=$id])))
Output
{
"users" : [ {
"title" : "foo",
"availability" : [ {
"id" : "online",
"state" : "valid"
}, {
"id" : "busy",
"state" : "unknown"
} ]
}, {
"title" : "bar",
"availability" : [ {
"id" : "busy",
"state" : "unknown"
}, {
"id" : "any",
"state" : "unknow",
"moreInfo" : "unavailable"
} ]
}, {
"title" : "baz",
"availability" : [ {
"id" : "any",
"state" : "unknow",
"moreInfo" : "unavailable"
} ]
} ]
}
(73616066)
[
{
"names": "Car",
"values": "Toyota"
},
{
"names": "Bike",
"values": "Schwinn"
},
{
"names": "Scooter",
"values": "Razor"
},
{
"names": "A0",
"values": "11"
},
{
"names": "A1",
"values": "12"
},
{
"names": "A2",
"values": "13"
},
{
"names": "B0",
"values": "2000"
},
{
"names": "B1",
"values": "4000"
},
{
"names": "B2",
"values": "22000"
}
]
Josson Query
@collect([names !=~ '\D\d+']*
.map(names::values)
,[names =~ '\D\d+']*
.group(names.substr(1), map(names::values))@
.elements
.mergeObjects()
.@toObject('Data')
)
.flatten(1)
.mergeObjects()
Output
{
"Car" : "Toyota",
"Bike" : "Schwinn",
"Scooter" : "Razor",
"Data" : [ {
"A0" : "11",
"B0" : "2000"
}, {
"A1" : "12",
"B1" : "4000"
}, {
"A2" : "13",
"B2" : "22000"
} ]
}
(72272928)
Dataset resp1
{
"data": {
"values": {
"name": "kiran",
"age": "24"
}
}
}
Dataset resp2
{
"data": {
"values": {
"name": "Mamu",
"age": "26"
}
}
}
Jossons Query
resp1->data.toArray() <+< resp2->data.toArray()
Output mergeResult
[ {
"name" : "kiran",
"age" : "24"
}, {
"name" : "Mamu",
"age" : "26"
} ]
Josson Query
toObject('values').toObject('data')
Output
{
"data" : {
"values" : [ {
"name" : "kiran",
"age" : "24"
}, {
"name" : "Mamu",
"age" : "26"
} ]
}
}
(47427518)
[
{
"router_id": "1101",
"floor_id": "20",
"building_id": "2",
"router_name": "1101"
},
{
"router_id": "1102",
"floor_id": "20",
"building_id": "2",
"router_name": "1102"
},
{
"router_id": "0",
"floor_id": "20",
"building_id": "2",
"router_name": "pancoordinator"
},
{
"router_id": "1104",
"floor_id": "20",
"building_id": "2",
"router_name": "1104"
}
]
Josson Query
group(building_id).map(
building_id,
floors: elements.group(floor_id).map(
floor_id,
routers: elements.map(
router_id, router_name)
)
)
.toObject('buildings')
Output
{
"buildings" : [ {
"building_id" : "2",
"floors" : [ {
"floor_id" : "20",
"routers" : [ {
"router_id" : "1101",
"router_name" : "1101"
}, {
"router_id" : "1102",
"router_name" : "1102"
}, {
"router_id" : "0",
"router_name" : "pancoordinator"
}, {
"router_id" : "1104",
"router_name" : "1104"
} ]
} ]
} ]
}
(51576987)
{
"values": [
{
"locale": "en_US",
"source_key": "book_format",
"value": "Hardback",
"display_attr_name": "Book Format",
"source_value": "Hardback",
"isPrimary": "true"
},
{
"isFacetValue": "true",
"facet_version": "1.1",
"locale": "en_US",
"value": "Hardcover"
}
]
}
Josson Query
values.mergeObjects()
Output
{
"locale" : "en_US",
"source_key" : "book_format",
"value" : "Hardcover",
"display_attr_name" : "Book Format",
"source_value" : "Hardback",
"isPrimary" : "true",
"isFacetValue" : "true",
"facet_version" : "1.1"
}
(73506183)
[ {
"count" : 15,
"_id" : {
"DB User Name" : "admin",
"Session Activity Type" : "LOGOFF"
}
}, {
"count" : 16,
"_id" : {
"DB User Name" : "dbuser",
"Session Activity Type" : "LOGON"
}
}, {
"count" : 17,
"_id" : {
"DB User Name" : "dbuser",
"Session Activity Type" : "LOGOFF"
}
}, {
"count" : 18,
"_id" : {
"DB User Name" : "admin",
"Session Activity Type" : "LOGON"
}
} ]
Josson Query
field(_id.field(DB User Name.if(equals('dbuser'),'updated-Admin',?)))
Output
[ {
"count" : 15,
"_id" : {
"DB User Name" : "admin",
"Session Activity Type" : "LOGOFF"
}
}, {
"count" : 16,
"_id" : {
"DB User Name" : "updated-Admin",
"Session Activity Type" : "LOGON"
}
}, {
"count" : 17,
"_id" : {
"DB User Name" : "updated-Admin",
"Session Activity Type" : "LOGOFF"
}
}, {
"count" : 18,
"_id" : {
"DB User Name" : "admin",
"Session Activity Type" : "LOGON"
}
} ]
(72790475)
[
{
"ID": "id1",
"ref": "ref1",
"categ": "CATEG_A",
"pagenb": 1
},
{
"ID": "id2",
"ref": "ref1",
"categ": "CATEG_A",
"pagenb": 2
},
{
"ID": "id3",
"ref": "ref1",
"categ": "CATEG_B",
"pagenb": 3
}
]
Josson Query
group(map(ref, categ), ID).map(ID, key.ref, key.categ)
Output
[ {
"ID" : [ "id1", "id2" ],
"ref" : "ref1",
"categ" : "CATEG_A"
}, {
"ID" : [ "id3" ],
"ref" : "ref1",
"categ" : "CATEG_B"
} ]
(73405994)
{
"cnpj": {
"numeroCNPJ": "string"
},
"codigoCNES": {
"codigo": "string"
},
"codigoUnidade": {
"codigo": "string"
},
"diretor": {
"cpf": {
"numeroCPF": "string"
},
"nome": {
"nome": "string"
}
},
"nomeEmpresarial": {
"nome": "string"
},
"nomeFantasia": {
"nome": "string"
}
}
Josson Query
entries().map(
key::value.if([size()=1], *, **.mergeObjects())
)
Output
{
"cnpj" : "string",
"codigoCNES" : "string",
"codigoUnidade" : "string",
"diretor" : {
"numeroCPF" : "string",
"nome" : "string"
},
"nomeEmpresarial" : "string",
"nomeFantasia" : "string"
}
(35438323)
[
{
"name": "Team Wolf",
"www": "http://www.teamwolf.qqq",
"department": "department1",
"team1": "team1"
},
{
"name": "Team Fox",
"www": "http://www.teamfox.qqq",
"department": "department1",
"team2": "team2"
},
{
"name": "Team Falcon",
"www": "http://www.teamfalcon.qqq",
"department": "department1",
"team3": "team3"
}
]
Josson Query
group(department).map(
department:: elements.map(
~'^team.*':: map(
name, www
)
)
.mergeObjects()
)
Output
[ {
"department1" : {
"team1" : {
"name" : "Team Wolf",
"www" : "http://www.teamwolf.qqq"
},
"team2" : {
"name" : "Team Fox",
"www" : "http://www.teamfox.qqq"
},
"team3" : {
"name" : "Team Falcon",
"www" : "http://www.teamfalcon.qqq"
}
}
} ]
(73397763)
{
"errorCount": 2,
"errorIndices": [
0,
1
],
"data": [
{
"errorCode": 901,
"errorMessage": "IBad data: Check the data",
"errorData": "xxxx"
},
{
"errorCode": 901,
"errorMessage": "IBad data: Check the data",
"errorData": "XZY"
},
"fun now"
]
}
Josson Query
field(data[isText()]*, errors: data[isObject()]*)
Output
{
"errorCount" : 2,
"errorIndices" : [ 0, 1 ],
"data" : [ "fun now" ],
"errors" : [ {
"errorCode" : 901,
"errorMessage" : "IBad data: Check the data",
"errorData" : "xxxx"
}, {
"errorCode" : 901,
"errorMessage" : "IBad data: Check the data",
"errorData" : "XZY"
} ]
}
(73224582)
{
"header": [
{
"key": "numberOfRecords",
"value": "122",
"valueDataType": "string"
},
{
"key": "g_udit",
"value": "1",
"valueDataType": "string"
},
{
"key": "userNameId",
"value": "155",
"valueDataType": "string"
}
]
}
Josson Query
map(header.map(key::value).mergeObjects())
Output
{
"header" : {
"numberOfRecords" : "122",
"g_udit" : "1",
"userNameId" : "155"
}
}
(73200231)
{
"data": {
"myProp": true,
"myAnother": true,
"myAnotherOne": false
}
}
Josson Query
map(values: data.entries().[value]*.key.upperSnakeCase())
Output
{
"values" : [ "MY_PROP", "MY_ANOTHER" ]
}
(73190751)
{
"data": [
{
"fieldname": "Name",
"fieldvalue": [
"John Doe"
]
},
{
"fieldname": "Title",
"fieldvalue": [
"Manager"
]
},
{
"fieldname": "Company",
"fieldvalue": [
"Walmart"
]
}
]
}
Josson Query
map(
finalPayload: map(
PI: map(
EmpName: data[fieldname='Name'].fieldvalue[0],
EmpRole: data[fieldname='Title'].fieldvalue[0]
),
Company: data[fieldname='Company'].fieldvalue[0]
)
)
Output
{
"finalPayload" : {
"PI" : {
"EmpName" : "JohnDoe",
"EmpRole" : "Manager"
},
"Company" : "Walmart"
}
}
(73131799)
Dataset left
{
"package": {
"institution": [
{
"school": "TP"
}
],
"people": [
{
"age": 32,
"name": "Bob"
},
{
"age": 16,
"name": "Amanda"
}
],
"details": [
{
"course": "Humanities",
"duration": 4,
"description": "Students in Computer Sci"
}
]
}
}
Dataset right
{
"package": {
"institution": [
{
"school": "MIT"
}
],
"people": [
{
"age": 32,
"name": "Bob"
},
{
"age": 16,
"name": "Samantha"
},
{
"age": 20,
"name": "Dylan"
}
],
"details": [
{
"course": "Computer Sci",
"duration": 4,
"description": "Students in Computer Sci"
}
]
}
}
Jossons Query
left->package >-> right->package
Output
{
"institution" : [ {
"school" : "MIT"
} ],
"people" : [ {
"age" : 16,
"name" : "Samantha"
}, {
"age" : 20,
"name" : "Dylan"
} ],
"details" : [ {
"course" : "Computer Sci",
"duration" : 4,
"description" : "Students in Computer Sci"
} ]
}
(72442001)
{
"new": {
"bc_sku_partner": [
"Amazon",
"Ebay"
],
"bc_sku_channel": [
"Partner",
"Online",
"Store"
],
"cc_sku_channel": [
"Store"
]
}
}
Josson Query
new
.entries()
.unwind(value)
.[key='bc_sku_partner' | value!='Partner']*
.map(catalog:key.substr(0,2).upperCase(),
channel:if([key='bc_sku_partner'],'Partner',value),
partner:if([key='bc_sku_partner'],value))
.toObject('catalogs')
Output
{
"catalogs" : [ {
"catalog" : "BC",
"channel" : "Partner",
"partner" : "Amazon"
}, {
"catalog" : "BC",
"channel" : "Partner",
"partner" : "Ebay"
}, {
"catalog" : "BC",
"channel" : "Online"
}, {
"catalog" : "BC",
"channel" : "Store"
}, {
"catalog" : "CC",
"channel" : "Store"
} ]
}
(72442443)
{
"id": "1234",
"recordType": "E",
"receiveDate": "2009-01-01",
"receiveTime": "09:55:00",
"releaseDate": "2009-01-02",
"releaseTime": "08:30:00",
"classifications": [
{
"reportType": 1031435,
"description": {
"string": "Progress Report"
}
},
{
"reportType": 8888888,
"description": {
"string": "Net Tangible Asset Backing"
}
}
],
"type": "ASX"
}
Josson Query
map(id,
recordType,
classifications
.map(reportType, description:description.string)
.wrap()
.collect([0],
ifNot([reportType=8888888], json('{"reportType":8888888,"description":"Default item"}')),
ifNot([reportType=9999999], json('{"reportType":9999999,"description":"Default item"}')))
.flatten(1),
type)
Output
{
"id" : "1234",
"recordType" : "E",
"classifications" : [ {
"reportType" : 1031435,
"description" : "Progress Report"
}, {
"reportType" : 8888888,
"description" : "Net Tangible Asset Backing"
}, {
"reportType" : 9999999,
"description" : "Default item"
} ],
"type" : "ASX"
}
(70753154)
{
\"group\": [
{
\"schema\": \"file\"
},
{
\"key1\": \"val1\",
\"key2\": \"val2\"
},
{
\"schema\": \"folder\"
},
{
\"key1\": \"val1\",
\"key2\": \"val2\",
\"key3\": \"val3\"
},
{
\"schema\": \"dir\"
},
{
\"key1\": \"val1\",
\"key2\": \"val2\",
\"key3\": \"val3\",
\"key4\": \"val4\"
}
]
}
Josson Query
group
.group(#.floor(calc(?/2)))@
.mergeObjects(elements)
.@toObject('group')
Output
{
"group" : [ {
"schema" : "file",
"key1" : "val1",
"key2" : "val2"
}, {
"schema" : "folder",
"key1" : "val1",
"key2" : "val2",
"key3" : "val3"
}, {
"schema" : "dir",
"key1" : "val1",
"key2" : "val2",
"key3" : "val3",
"key4" : "val4"
} ]
}
(54502431)
{
"entry": [
{
"resource": {
"id": "car-1",
"type": "vehicle",
"color": "red",
"owner": {
"ref": "Person/person-1"
}
}
},
{
"resource": {
"id": "car-2",
"type": "vehicle",
"color": "blue",
"owner": {
"ref": "Person/person-2"
}
}
},
{
"resource": {
"id": "person-1",
"type": "Person",
"name": "john"
}
},
{
"resource": {
"id": "person-2",
"type": "Person",
"name": "wick"
}
}
]
}
Josson Query
entry
.resource
.group(if([type='vehicle'], owner.ref.removeStart('Person/'), id),
if([type='vehicle'], field(owner:), map(ownername:name)))@
.elements
.mergeObjects()
Output
[ {
"id" : "car-1",
"type" : "vehicle",
"color" : "red",
"ownername" : "john"
}, {
"id" : "car-2",
"type" : "vehicle",
"color" : "blue",
"ownername" : "wick"
} ]
(73884462)
[
{
"coupon": "VAR",
"currency": "USD",
"sip": "94989WAX5",
"lastModifiedDate": "2022-09-23T08:16:25Z"
},
{
"coupon": "VAR1",
"currency": "USD",
"sip": "94989WAX5",
"lastModifiedDate": "2022-09-21T08:16:25Z"
},
{
"coupon": "VAR3",
"currency": "USD",
"sip": "XHBRYWEB1",
"lastModifiedDate": "2022-09-20T08:16:25Z"
}
]
Josson Query
group(sip)@.elements.findByMax(lastModifiedDate)
Output
[ {
"coupon" : "VAR",
"currency" : "USD",
"sip" : "94989WAX5",
"lastModifiedDate" : "2022-09-23T08:16:25Z"
}, {
"coupon" : "VAR3",
"currency" : "USD",
"sip" : "XHBRYWEB1",
"lastModifiedDate" : "2022-09-20T08:16:25Z"
} ]