Josson & Jossons
- Josson is a query language for JSON.
- Jossons is a template engine to generate text output.
Features and Capabilities of Josson
- Query data from a JSON.
- Restructure a JSON.
- Has many functions to format text output.
- Has many functions to manipulate date values.
- Has many functions to work on array node.
- Can be used as an API parameter to trim down the response JSON result.
Features and Capabilities of Jossons
- Query data from multiple JSON datasets.
- Join two JSON datasets to build a new dataset.
- Resolve template placeholder from external data source on demand.
- I used Jossons to generate millions of SMS/Email notifications during the first year.
- I used Jossons to generate reports that retrieve data from MongoDB directly without writing a line of code.
Table of Contents
- Josson Basic
- Josson Path Chart Elements
- Josson Query Language
- Josson Functions
- Arithmetic Functions
- String Functions
- Date Functions
- Format Functions
- Logical Functions
- Array Functions
- Structural Functions
- Jossons Basic
- Jossons Template Language
- Ternary Syntax
- Implicit Variables
- Fill In
- Jossons Resolver
- Dictionary Finder
- Data Finder
- Join Datasets
- Appendix
- MongoDB Adapter
Josson Basic
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(jossonPath);
Josson Path Chart Elements
Josson path chart shows data type changes and data flow along the path. Data filtering, transformation and formatting details are not included.
% Any JSON node
[%] Array node
{} Object node
"" Text node
$I Integer node
$D Double node
$TF Boolean node
null Null node
$V Any value node ""|$I|$D|$TF|null
$V... Multiple value nodes inside function arguments
-> A path step progress
obj{} A named object node
{}.% Object parent-child relation is connected by a "." (Except the branch's 1st step)
{}[]->{} An object with validation filter
[%]*->[%] An array node proceeded one step with all its elements
[%][]->% An array node with find-first filter
[%][]*->[%] An array node with find-all filter
%->
/ An array node with find-all filter and
[%][]@ divert each filtered element to separate branch
\
%->
->func()-> A function that manipulate the current node
->[%->func()]-> A function that manipulate each element of an array node
func(?) Function parameter symbol "?" represents the current node
.--->---.
/ \ Function parameter symbol "@" represents the parent array node
[]->[%->func(@)]
[[%]]->[%] Nested array is flattened each step
%->
/
->[]@ Divert each array element to separate branch
\
%->
->%
\
@->[%] Merge branches into an array
/
->%
==>% Final result
->%
\
==>[%] Merge branches to a final result
/
->%
!! The position where the step is unresolvable
==>!unresolvable! Unable to resolve the result (Returns a Java null value)
Josson Query Language
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{} ==>{}
-
Object parent-child relation is 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.
A path argument takes the function's current node as its parent.josson.getNode("customer.UPPERCase(name)") ==> "PEGGY"
Path chart
{}->customer{}->UPPERCase($V) ==>""
-
If the function is the first path step, it works on the root node.
josson.getNode("upperCase(customer.name)") ==> "PEGGY"
Path chart
{}->upperCase($V) ==>""
-
Functions can be nested and the parameters have the same parent node.
josson.getNode("customer.concat(upperCase(name), ' / ', phone)") ==> "PEGGY / +852 62000610"
Path chart
{}->customer{}->concat($V...) ==>""
-
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.
To query an array element by 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[0] ==>{}
-
To query a value node in an array element.
josson.getNode("items[1].name") ==> "OctoPlus Tennis Racket - Star"
Path chart
{}->items[1].name ==>""
-
To query an object node in an array element.
josson.getNode("items[2].property") ==> { "size" : "35", "colors" : [ "RED" ] }
Path chart
{}->items[2].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($V)] ==>[""]
-
For function argument, a path step
?
represents the current node.josson.getNode("items.qty.concat('Qty=',?)") ==> [ "Qty=2", "Qty=1", "Qty=1" ]
Path chart
{}->items*->[qty]->[$I->concat(?)] ==>[""]
-
A function that 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([$V])->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] ==>[""]
-
For each path step, a nested array is flattened once.
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*->[""]] ==>[""]
-
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*->[""]
-
Some 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()->""
-
Syntax
[]@
can divert the array output of 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()
-
Function parameter can be a value node of parent.
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($V...)->"" / \ {}->items@ @->[""]->join()==>"" \ / {}->repeat($V...)->""
-
Functions work on array and produce an array, such as
concat()
, manipulate on each element.
An argument#
denotes the zero-based array index.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(#, $V...)]->join() ==>""
-
An argument
##
denotes the one-based array index.
A function argument path step start with@
represents the parent array node.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($V)->[{}->concat(@, ##, $V...)]->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 with a validation filter.
josson.getNode("customer[name='Raymond']") ==> !unresolvable!
Path chart
{}->customer[]!! ==>!unresolvable!
-
Function
json()
parse a JSON string.josson.getNode("json('[1,2,"3"]')") ==> [ 1, 2, "3" ]
Path chart
[] ==>[]
-
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($V...)]->[$D->concat(?, ##)] ==>[""]
-
Non-array manipulate functions preserve null element.
josson.getNode("items.calc(qty * (unitPrice-unitDiscount)).[##<=2]*.concat(##,'=',?)") ==> [ null, "2=140.0" ]
Path chart
{}->items*->[{}->calc($V...)]->[$D][]*->[$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($V...)]->[$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($V...)]->[$D][]*->[$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($V...)]->[$D][]*->[$D->concat(?, #A)]->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($V...)->$D / \ {}->items@ @->[$D->concat(?, #a)] ==>[""] \ / {}->calc($V...)->$D
-
mXparser expression accepts single-level path only. To apply multi-level 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($V...)]->[$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[0]->{}->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
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
{}->customer->toArray($V...) ==>[""]
-
Function
map()
constructs a new object node. For multi-level path, the last element name will become the new element name. To rename an element, use syntaxnewFieldName: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($V...) ==>{}
-
Function
field()
adds, removes and renames field on the current object node. To remove an element, use syntaxfieldName:
.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($V...) ==>{}
-
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($V...)] ==>[{}]
-
Function
flatten()
flatten an array same as the default path step behavior. But more readable.josson.getNode("[email protected]") ==> [ [ "SHIRT", "WOMEN" ], [ "TENNIS", "SPORT", "RACKET" ], [ "SHOE", "SPORT", "WOMEN" ] ] josson.getNode("[email protected].@flatten()") ==> [ "SHIRT", "WOMEN", "TENNIS", "SPORT", "RACKET", "SHOE", "SPORT", "WOMEN" ] josson.getNode("[email protected].@[true]*") ==> [ "SHIRT", "WOMEN", "TENNIS", "SPORT", "RACKET", "SHOE", "SPORT", "WOMEN" ]
Path chart
{}->tags*->[""] / \ {}->items@ @->[[""]]->flatten() ==>[""] \ / {}->tags*->[""]
Josson Functions
There are over 180 functions. They are classified into categories:
Arithmetic Functions
- abs()
- calc()
- ceil()
- floor()
- mod()
- round()
String Functions
- abbreviate()
- appendIfMissing()
- appendIfMissingIgnoreCase()
- capitalize()
- center()
- concat()
- keepAfter()
- keepAfterIgnoreCase()
- keepAfterLast()
- keepAfterLastIgnoreCase()
- keepBefore()
- keepBeforeIgnoreCase()
- keepBeforeLast()
- keepBeforeLastIgnoreCase()
- leftPad()
- length()
- lowerCase()
- notEmpty()
- notBlank()
- prependIfMissing()
- prependIfMissingIgnoreCase()
- removeEnd()
- removeEndIgnoreCase()
- removeStart()
- removeStartIgnoreCase()
- repeat()
- replace()
- replaceIgnoreCase()
- rightPad()
- split()
- strip()
- stripEnd()
- stripStart()
- substr()
- trim()
- uncapitalize()
- upperCase()
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()
- localToOffsetDate()
- offsetToLocalDate()
Format Functions
- b64Encode()
- b64EncodeNoPadding()
- b64MimeEncode()
- b64MimeEncodeNoPadding()
- b64UrlEncode()
- b64UrlEncodeNoPadding()
- b64Decode()
- b64MimeDecode()
- b64UrlDecode()
- urlEncode()
- urlDecode()
- caseValue()
- indexedValue()
- cycleValue()
- formatDate()
- formatNumber()
- formatText()
- formatTexts()
- toNumber()
- toString()
- toText()
Logical Functions
- contains()
- containsIgnoreCase()
- notContains()
- notContainsIgnoreCase()
- startsWith()
- startsWithIgnoreCase()
- notStartsWith()
- notStartsWithIgnoreCase()
- endsWith()
- endsWithIgnoreCase()
- notEndsWith()
- notEndsWithIgnoreCase()
- equals()
- equalsIgnoreCase()
- notEquals()
- notEqualsIgnoreCase()
- in()
- inIgnoreCase()
- notIn()
- notInIgnoreCase()
- isEmpty()
- isNotEmpty()
- isBlank()
- isNotBlank()
- isNull()
- isNotNull()
- isText()
- isBoolean()
- isNumber()
- isEven()
- isOdd()
- not()
- isWeekday()
- isWeekend()
- isLeapYear()
Array Functions
- size()
- lastIndex()
- indexOf()
- lastIndexOf()
- first()
- last()
- max()
- min()
- sum()
- avg()
- count()
- reverse()
- slice()
- sort()
- distinct()
- join()
- findByMax()
- findByMin()
- findByNullOrMax()
- findByNullOrMin()
- findByMaxOrNull()
- findByMinOrNull()
Structural Functions
- json()
- entries()
- keys()
- toArray()
- flatten()
- map()
- field()
- coalesce()
- csv()
Following are some examples of each function.
Arithmetic Functions
1. abs()
-3.14.abs() ==> 3.14
abs(3.14) ==> 3.14
2. calc()
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. ceil()
3.14.ceil() ==> 4
ceil(-3.14) ==> -3
4. floor()
3.14.floor() ==> 3
floor(-3.14) ==> -4
5. mod()
8.mod(3) ==> 2
8.mod(?, 3) ==> 2
mod(-8, 3) ==> 1
3.mod(-8, ?) ==> 1
6. round()
3.14.round(1) ==> 3.1
3.14.round(?, 1) ==> 3.1
round(3.56, 0) ==> 4
String Functions
7. abbreviate()
'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"
8. appendIfMissing()
'abc'.appendIfMissing('xyz') ==> "abcxyz"
'abc'.appendIfMissing(?, 'xyz') ==> "abcxyz"
appendIfMissing('abcxyz', 'xyz') ==> "abcxyz"
'xyz'.appendIfMissing('abcXYZ', ?) ==> "abcXYZxyz"
9. appendIfMissingIgnoreCase()
'abc'.appendIfMissingIgnoreCase('xyz') ==> "abcxyz"
'abc'.appendIfMissingIgnoreCase(?, 'xyz') ==> "abcxyz"
appendIfMissingIgnoreCase('abcxyz', 'xyz') ==> "abcxyz"
'xyz'.appendIfMissingIgnoreCase('abcXYZ', ?) ==> "abcXYZ"
10. capitalize()
'cat'.capitalize() ==> "Cat"
capitalize('cAt') ==> "CAt"
11. center()
'abc'.center(7) ==> " abc "
'abc'.center(7, 'X') ==> "XXabcXX"
'abc'.center(?, 7, upperCase(?)) ==> "ABabcAB"
center('abc', 7, '') ==> " abc "
4.center('a', ?, 'yz') ==> "yayz"
12. concat()
'Hello'.concat(2022, '... ', ?, ' World!') ==> "2022... Hello World!"
13. keepAfter()
'abcxmnxyz'.keepAfter('x') ==> "mnxyz"
'abcxmnxyz'.keepAfter(?, 'X') ==> ""
keepAfter('abcxmnxyz', 'mn') ==> "xyz"
14. keepAfterIgnoreCase()
'abcxmnxyz'.keepAfterIgnoreCase('x') ==> "mnxyz"
'abcxmnxyz'.keepAfterIgnoreCase(?, 'X') ==> "mnxyz"
keepAfterIgnoreCase('abcxmnxyz', 'mn') ==> "xyz"
15. keepAfterLast()
'abcxmnxyz'.keepAfterLast('x') ==> "yz"
'abcxmnxyz'.keepAfterLast(?, 'X') ==> ""
keepAfterLast('abcxmnxyz', 'mn') ==> "xyz"
16. keepAfterLastIgnoreCase()
'abcxmnxyz'.keepAfterLastIgnoreCase('x') ==> "yz"
'abcxmnxyz'.keepAfterLastIgnoreCase(?, 'X') ==> "yz"
keepAfterLastIgnoreCase('abcxmnxyz', 'mn') ==> "xyz"
17. keepBefore()
'abcxmnxyz'.keepBefore('x') ==> "abc"
'abcxmnxyz'.keepBefore(?, 'X') ==> ""
keepBefore('abcxmnxyz', 'mn') ==> "abcx"
18. eepBeforeIgnoreCase()
'abcxmnxyz'.keepBeforeIgnoreCase('x') ==> "abc"
'abcxmnxyz'.keepBeforeIgnoreCase(?, 'X') ==> "abc"
keepBeforeIgnoreCase('abcxmnxyz', 'mn') ==> "abcx"
19. keepBeforeLast()
'abcxmnxyz'.keepBeforeLast('x') ==> "abcxmn"
'abcxmnxyz'.keepBeforeLast(?, 'X') ==> ""
keepBeforeLast('abcxmnxyz', 'mn') ==> "abcx"
20. keepBeforeLastIgnoreCase()
'abcxmnxyz'.keepBeforeLastIgnoreCase('x') ==> "abcxmn"
'abcxmnxyz'.keepBeforeLastIgnoreCase(?, 'X') ==> "abcxmn"
keepBeforeLastIgnoreCase('abcxmnxyz', 'mn') ==> "abcx"
21. leftPad()
'bat'.leftPad(5) ==> " bat"
'bat'.leftPad(?, 8, 'yz') ==> "yzyzybat"
leftPad('bat', 3, 'yz') ==> "bat"
5.leftPad('bat', ?, '') ==> " bat"
22. length()
'Josson'.length() ==> 6
length('Josson') ==> 6
length(2022) ==> 4
23. lowerCase()
'Cat'.lowerCase() ==> "cat"
lowerCase('cAt') ==> "cat"
24. notEmpty()
'abc'.notEmpty('xyz') ==> "abc"
''.notEmpty(null, '', 'xyz') ==> "xyz"
json('{"a":"","b":"","c":"abc"}').notEmpty(a,b,c,'xyz') ==> "abc"
25. notBlank()
'abc'.notBlank('xyz') ==> "abc"
' '.notBlank(null, ' ', 'xyz') ==> "xyz"
json('{"a":" ","b":" ","c":"abc"}').notBlank(a,b,c,'xyz') ==> "abc"
26. prependIfMissing()
'abc'.prependIfMissing('xyz') ==> "xyzabc"
'abc'.prependIfMissing(?, 'xyz') ==> "xyzabc"
prependIfMissing('xyzabc', 'xyz') ==> "xyzabc"
'xyz'.prependIfMissing('XYZabc', ?) ==> "xyzXYZabc"
27. prependIfMissingIgnoreCase()
'abc'.prependIfMissingIgnoreCase('xyz') ==> "xyzabc"
'abc'.prependIfMissingIgnoreCase(?, 'xyz') ==> "xyzabc"
prependIfMissingIgnoreCase('xyzabc', 'xyz') ==> "xyzabc"
'xyz'.prependIfMissingIgnoreCase('XYZabc', ?) ==> "XYZabc"
28. removeEnd()
'www.domain.com'.removeEnd('.com') ==> "www.domain"
'www.domain.com'.removeEnd(?, '.Com') ==> "www.domain.com"
removeEnd('www.domain.com', '.com') ==> "www.domain"
29. removeEndIgnoreCase()
'www.domain.COM'.removeEndIgnoreCase('.com') ==> "www.domain"
'www.domain.com'.removeEndIgnoreCase(?, '.Com') ==> "www.domain"
removeEndIgnoreCase('www.domain.com', '.COM') ==> "www.domain"
30. removeStart()
'www.domain.com'.removeStart('www.') ==> "domain.com"
'www.domain.com'.removeStart(?, '.Www') ==> "www.domain.com"
removeStart('www.domain.com', 'www.') ==> "domain.com"
31. removeStartIgnoreCase()
'WWW.domain.com'.removeStartIgnoreCase('www.') ==> "domain.com"
'www.domain.com'.removeStartIgnoreCase(?, '.Www') ==> "www.domain.com"
removeStartIgnoreCase('www.domain.com', 'WWW.') ==> "domain.com"
32. repeat()
'a'.repeat(3) ==> "aaa"
'ab'.repeat(?, 2) ==> "abab"
repeat('abc', 2) ==> "abcabc"
3.repeat('abc', ?) ==> "abcabcabc"
33. replace()
'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"
34. replaceIgnoreCase()
'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"
35. rightPad()
'bat'.rightPad(5) ==> "bat "
'bat'.rightPad(?, 8, 'yz') ==> "batyzyzy"
rightPad('bat', 3, 'yz') ==> "bat"
rightPad('bat', 5, '') ==> "bat "
36. split()
'abc def'.split() ==> [ "abc", "def" ]
'abc def'.split(' ') ==> [ "abc", "def" ]
' abc def '.split(?, ' ') ==> [ "abc", "def" ]
split('ab:cd:ef', ':') ==> [ "ab", "cd", "ef" ]
37. strip()
' abc '.strip(' ') ==> "abc"
' abcyx'.strip('xyz') ==> " abc"
strip('z abcyx', 'xyz') ==> " abc"
38. stripEnd()
' abc '.stripEnd(' ') ==> " abc"
'z abcyx'.stripEnd('xyz') ==> "z abc"
stripEnd('z abcyx', 'xyz') ==> "z abc"
39. stripStart()
' abc '.stripStart(' ') ==> "abc "
'z abcyx'.stripStart('xyz') ==> " abcyx"
stripStart('z abcyx', 'xyz') ==> " abcyx"
40. substr()
'abc'.substr(1) ==> "bc"
'abc'.substr(0, 2) ==> "ab"
'abc'.substr(?, 1, 2) ==> "b"
substr('abc', -2, -1) ==> "b"
2.substr('abc', -4, ?) ==> "ab"
41. trim()
'abc'.trim() ==> "abc"
trim(' abc ') ==> "abc"
42. uncapitalize()
'Cat'.uncapitalize() ==> "cat"
uncapitalize('CAt') ==> "cAt"
43. upperCase()
'Cat'.upperCase() ==> "CAT"
upperCase('cAt') ==> "CAT"
Date Functions
44. amPmOfDay()
'2022-01-02T03:04:05'.amPmOfDay() ==> "AM"
amPmOfDay('2022-02-04T13:14:15') ==> "PM"
45. second()
'2022-01-02T03:04:05'.second() ==> 5
second('2022-02-04T13:14:15') ==> 15
46. secondOfDay()
'2022-01-02T03:04:05'.secondOfDay() ==> 11045
secondOfDay('2022-02-04T13:14:15') ==> 47655
47. minute()
'2022-01-02T03:04:05'.minute() ==> 4
minute('2022-02-04T13:14:15') ==> 14
48. minuteOfDay()
'2022-01-02T03:04:05'.minuteOfDay() ==> 184
minuteOfDay('2022-02-04T13:14:15') ==> 794
49. hourOfAmPm()
'2022-01-02T03:04:05'.hourOfAmPm() ==> 3
hourOfAmPm('2022-02-04T13:14:15') ==> 1
50. hour()
'2022-01-02T03:04:05'.hour() ==> 3
hour('2022-02-04T13:14:15') ==> 13
51. dayOfWeek()
'2022-01-02T03:04:05'.dayOfWeek() ==> 7
dayOfWeek('2022-02-04T13:14:15') ==> 5
52. day()
'2022-01-02T03:04:05'.day() ==> 2
day('2022-02-04T13:14:15') ==> 4
53. dayOfYear()
'2022-01-02T03:04:05'.dayOfYear() ==> 2
dayOfYear('2022-02-04T13:14:15') ==> 35
54. month()
'2022-01-02T03:04:05'.month() ==> 1
month('2022-02-04T13:14:15') ==> 2
55. year()
'2022-01-02T03:04:05'.year() ==> 2022
year('2022-02-04T13:14:15') ==> 2022
56. plusSeconds()
'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"
57. plusMinutes()
'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"
58. plusHours()
'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"
59. plusDays()
'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"
60. plusWeeks()
'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"
61. plusMonths()
'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"
62. plusYears()
'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"
63. minusSeconds()
'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"
64. minusMinutes()
'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"
65. minusHours()
'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"
66. minusDays()
'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"
67. minusWeeks()
'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"
68. minusMonths()
'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"
69. minusYears()
'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"
70. truncateToMicro()
'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"
71. truncateToMilli()
'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"
72. truncateToSecond()
'2022-01-02T03:04:05.229390600'.truncateToSecond() ==> "2022-01-02T03:04:05"
truncateToSecond('2022-02-04T13:14:15.229390600') ==> "2022-02-04T13:14:15"
73. truncateToMinute()
'2022-01-02T03:04:05.229390600'.truncateToMinute() ==> "2022-01-02T03:04"
truncateToMinute('2022-02-04T13:14:15.229390600') ==> "2022-02-04T13:14"
74. truncateToHour()
'2022-01-02T03:04:05.229390600'.truncateToHour() ==> "2022-01-02T03:00"
truncateToHour('2022-02-04T13:14:15.229390600') ==> "2022-02-04T13:00"
75. truncateToDay()
'2022-01-02T03:04:05.229390600'.truncateToDay() ==> "2022-01-02T00:00"
truncateToDay('2022-02-04T13:14:15.229390600') ==> "2022-02-04T00:00"
76. truncateToMonth()
'2022-01-02T03:04:05.229390600'.truncateToMonth() ==> "2022-01-01T00:00"
truncateToMonth('2022-02-04T13:14:15.229390600') ==> "2022-02-01T00:00"
77. truncateToYear()
'2022-01-02T03:04:05.229390600'.truncateToYear() ==> "2022-01-01T00:00"
truncateToYear('2022-02-04T13:14:15.229390600') ==> "2022-01-01T00:00"
78. withNano()
'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"
79. withMicro()
'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"
80. withMilli()
'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"
81. withSecond()
'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"
82. withMinute()
'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"
83. withHour()
'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"
84. withDay()
'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"
85. withDayOfYear()
'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"
86. withMonth()
'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"
87. withYear()
'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"
88. dayEnd()
'2022-01-02T03:04'.dayEnd() ==> "2022-01-02T23:59:59.999999999"
dayEnd('2022-02-04T13:14') ==> "2022-02-04T23:59:59.999999999"
89. monthEnd()
'2022-01-02T03:04'.monthEnd() ==> "2022-01-31T23:59:59.999999999"
monthEnd('2022-02-04T13:14') ==> "2022-02-28T23:59:59.999999999"
90. yearEnd()
'2022-01-02T03:04'.yearEnd() ==> "2022-12-31T23:59:59.999999999"
yearEnd('2022-02-04T13:14') ==> "2022-12-31T23:59:59.999999999"
91. lengthOfMonth()
'2022-01-02T03:04'.lengthOfMonth() ==> 31
lengthOfMonth('2022-02-04T13:14') ==> 28
92. lengthOfYear()
'2022-01-02T03:04'.lengthOfYear() ==> 365
lengthOfYear('2024-02-04T13:14') ==> 366
93. localToOffsetDate()
'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"
94. offsetToLocalDate()
'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"
Format Functions
95. b64Encode()
'abcdefghijklmnopqrstuvwxyz~!@#$%^&*()_+-=ABCDEFGHIJKLMNOPQRSTUVWXYZ'.b64Encode()
==>
"YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXp+IUAjJCVeJiooKV8rLT1BQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWg=="
96. b64EncodeNoPadding()
b64EncodeNoPadding('abcdefghijklmnopqrstuvwxyz~!@#$%^&*()_+-=ABCDEFGHIJKLMNOPQRSTUVWXYZ')
==>
"YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXp+IUAjJCVeJiooKV8rLT1BQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWg"
97. b64MimiEncode() - Split lines into 76 character wide chunks
'abcdefghijklmnopqrstuvwxyz~!@#$%^&*()_+-=ABCDEFGHIJKLMNOPQRSTUVWXYZ'.b64MimeEncode()
==>
"YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXp+IUAjJCVeJiooKV8rLT1BQkNERUZHSElKS0xNTk9Q\r\nUVJTVFVWV1hZWg=="
98. b64MimeEncodeNoPadding()
b64MimeEncodeNoPadding('abcdefghijklmnopqrstuvwxyz~!@#$%^&*()_+-=ABCDEFGHIJKLMNOPQRSTUVWXYZ')
==>
"YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXp+IUAjJCVeJiooKV8rLT1BQkNERUZHSElKS0xNTk9Q\r\nUVJTVFVWV1hZWg"
99. b64UrlEncode()
'abcdefghijklmnopqrstuvwxyz~!@#$%^&*()_+-=ABCDEFGHIJKLMNOPQRSTUVWXYZ'.b64UrlEncode()
==>
"YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXp-IUAjJCVeJiooKV8rLT1BQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWg=="
100. b64UrlEncodeNoPadding()
b64UrlEncodeNoPadding('abcdefghijklmnopqrstuvwxyz~!@#$%^&*()_+-=ABCDEFGHIJKLMNOPQRSTUVWXYZ')
==>
"YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXp-IUAjJCVeJiooKV8rLT1BQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWg"
101. b64Decode()
'YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXp+IUAjJCVeJiooKV8rLT1BQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWg=='.b64Decode()
==>
"abcdefghijklmnopqrstuvwxyz~!@#$%^&*()_+-=ABCDEFGHIJKLMNOPQRSTUVWXYZ"
b64Decode('YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXp+IUAjJCVeJiooKV8rLT1BQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWg')
==>
"abcdefghijklmnopqrstuvwxyz~!@#$%^&*()_+-=ABCDEFGHIJKLMNOPQRSTUVWXYZ"
102. b64MimeDecode()
'YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXp+IUAjJCVeJiooKV8rLT1BQkNERUZHSElKS0xNTk9Q\nUVJTVFVWV1hZWg=='.b64MimeDecode()
==>
"abcdefghijklmnopqrstuvwxyz~!@#$%^&*()_+-=ABCDEFGHIJKLMNOPQRSTUVWXYZ"
b64MimeDecode('YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXp+IUAjJCVeJiooKV8rLT1BQkNERUZHSElKS0xNTk9Q\r\nUVJTVFVWV1hZWg')
==>
"abcdefghijklmnopqrstuvwxyz~!@#$%^&*()_+-=ABCDEFGHIJKLMNOPQRSTUVWXYZ"
103. b64UrlDecode()
'YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXp-IUAjJCVeJiooKV8rLT1BQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWg=='.b64UrlDecode()
==>
"abcdefghijklmnopqrstuvwxyz~!@#$%^&*()_+-=ABCDEFGHIJKLMNOPQRSTUVWXYZ"
b64UrlDecode('YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXp-IUAjJCVeJiooKV8rLT1BQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWg')
==>
"abcdefghijklmnopqrstuvwxyz~!@#$%^&*()_+-=ABCDEFGHIJKLMNOPQRSTUVWXYZ"
104. urlEncode()
'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"
105. urlDecode()
'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"
106. caseValue()
'a'.caseValue('c',1,'b',2,'a',3,4) ==> 3
'z'.caseValue('c',1,'b',2,'a',3,4) ==> 4
'z'.caseValue('c',1,'b',2,'a',3) ==> !unresolvable!
json('[{"s":1},{"s":null},{"s":3}]').s.caseValue(1,'A',null,'B') ==> [ "A", "B", null ]
107. indexedValue()
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!
108. cycleValue()
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"
109. formatDate()
'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"
110. formatNumber()
12345.6.formatNumber('HK$#,##0.00') ==> "HK$12,345.60"
123.formatNumber(?, '#,##0.#') ==> "123"
formatNumber(123.45, '#,##0.#') ==> "123.5"
111. formatText()
'Dog'.formatText('[%-5s]') ==> "[Dog ]"
123.formatText(?, '[%5d]') ==> "[ 123]"
formatText('Dog', '[%5s]') ==> "[ Dog]"
112. formatTexts()
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"
113. toNumber()
'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 ]
114. toString()
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]"
115. toText()
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" ]
Logical
116. contains()
'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
117. containsIgnoreCase()
'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
118. notContains()
'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
119. notContainsIgnoreCase()
'abcde'.notContainsIgnoreCase('bc') ==> false
notContainsIgnoreCase('abcde','B') ==> false
json('["a","b","c"]').notContainsIgnoreCase(?, 'D') ==> true
120. startsWith()
'abcdef'.startsWith('abc') ==> true
'ABCDEF'.startsWith(?,'abc') ==> false
startsWith('ABCDEF','cde') ==> false
121. startsWithIgnoreCase()
'abcdef'.startsWithIgnoreCase('abc') ==> true
'ABCDEF'.startsWithIgnoreCase(?,'abc') ==> true
startsWithIgnoreCase('ABCDEF','cde') ==> false
122. notStartsWith()
'abcdef'.notStartsWith('abc') ==> false
'ABCDEF'.notStartsWith(?,'abc') ==> true
notStartsWith('ABCDEF','cde') ==> true
123. notStartsWithIgnoreCase()
'abcdef'.notStartsWithIgnoreCase('abc') ==> false
'ABCDEF'.notStartsWithIgnoreCase(?,'abc') ==> false
notStartsWithIgnoreCase('ABCDEF','cde') ==> true
124. endsWith()
'abcdef'.endsWith('def') ==> true
'ABCDEF'.endsWith(?,'def') ==> false
endsWith('ABCDEF','cde') ==> false
125. endsWithIgnoreCase()
'abcdef'.endsWithIgnoreCase('def') ==> true
'ABCDEF'.endsWithIgnoreCase(?,'def') ==> true
endsWithIgnoreCase('ABCDEF','cde') ==> false
126. notEndsWith()
'abcdef'.notEndsWith('def') ==> false
'ABCDEF'.notEndsWith(?,'def') ==> true
notEndsWith('ABCDEF','cde') ==> true
127. notEndsWithIgnoreCase()
'abcdef'.notEndsWithIgnoreCase('def') ==> false
'ABCDEF'.notEndsWithIgnoreCase(?,'def') ==> false
notEndsWithIgnoreCase('ABCDEF','cde') ==> true
128. equals()
'abc'.equals('abc') ==> true
'abc'.equals(?, ' abc') ==> false
equals('ABC','abc') ==> false
129. equalsIgnoreCase()
'abc'.equalsIgnoreCase('abc') ==> true
'abc'.equalsIgnoreCase(?, ' abc') ==> false
equalsIgnoreCase('ABC','abc') ==> true
130. notEquals()
'abc'.notEquals('abc') ==> false
'abc'.notEquals(?, ' abc') ==> true
notEquals('ABC','abc') ==> true
131. notEqualsIgnoreCase()
'abc'.notEqualsIgnoreCase('abcd') ==> true
'abc'.notEqualsIgnoreCase(' abc') ==> true
notEqualsIgnoreCase('ABC','abc') ==> false
132. in()
56.in(12,34,56) ==> true
'56'.in(12,34,56) ==> true
'A'.in(json('["a","b","c"]')) ==> false
133. inIgnoreCase()
'A'.inIgnoreCase('a','b','c') ==> true
'a '.inIgnoreCase('a','b','c') ==> false
134. notIn()
56.notIn(12,34,56) ==> false
'56'.notIn(12,34,56) ==> false
'A'.notIn(json('["a","b","c"]')) ==> true
135. notInIgnoreCase()
'A'.notInIgnoreCase('a','b','c') ==> false
'a '.notInIgnoreCase('a','b','c') ==> true
136. isEmpty()
''.isEmpty() ==> true
isEmpty(' ') ==> false
137. isNotEmpty()
''.isNotEmpty() ==> false
isNotEmpty(' ') ==> true
138. isBlank()
''.isBlank() ==> true
isBlank(' ') ==> true
139. isNotBlank()
''.isNotBlank() ==> false
isNotBlank(' ') ==> false
140. isNull()
null.isNull() ==> !unresolvable!
isNull(null) ==> true
isNull('') ==> false
141. isNotNull()
null.isNotNull() ==> !unresolvable!
isNotNull(null) ==> false
isNotNull('') ==> true
142. isText()
'text'.isText() ==> true
isText(1) ==> false
isText(true) ==> false
isText(null) ==> false
143. isBoolean()
'text'.isBoolean() ==> false
isBoolean(1) ==> false
isBoolean(true) ==> true
isBoolean(null) ==> false
144. isNumber()
'text'.isNumber() ==> false
isNumber(1) ==> true
isNumber(true) ==> false
isNumber(null) ==> false
145. isEven()
1.isEven() ==> false
isEven(2) ==> true
146. isOdd()
1.isOdd() ==> true
isOdd(2) ==> false
147. not()
true.not() ==> false
not(false) ==> true
not('false') ==> false
not(0) ==> false
not(null) ==> false
148. isWeekday
'2021-12-31T00:00:00'.isWeekday() ==> true
isWeekday('2022-01-01T00:00:00') ==> false
149. isWeekend
'2021-12-31T00:00:00'.isWeekend() ==> false
isWeekend('2022-01-01T00:00:00') ==> true
150. isLeapYear
'2020-12-31T00:00:00'.isLeapYear() ==> true
isLeapYear('2022-01-01T00:00:00') ==> false
Array Functions
151. size()
json('[7,1,9,null,5,3]').size() ==> 6
size(json('[7,1,9,null,5,3]')) ==> 6
152. lastIndex()
json('[7,1,9,null,5,3]').lastIndex() ==> 5
lastIndex(json('[7,1,9,null,5,3]')) ==> 5
153. indexOf()
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
154. lastIndexOf()
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
155. first()
json('[7,1,9,null,5,3]').first() ==> 7
first(json('[null,7,1,9,5,3]')) ==> null
156. last()
json('[7,1,9,null,5,3]').last() ==> 3
last(json('[7,1,9,5,3,null]')) ==> null
157. max()
json('[7,1,9,null,5,3]').max() ==> 9
max(json('[7,1,9,null,5,3]'), 15, 16) ==> 16
158. min()
json('[7,1,9,null,5,3]').min() ==> 1
min(json('[7,1,9,null,5,3]'), 15, 16) ==> 1
159. sum()
json('[7,1,9,null,5,3]').sum() ==> 25.0
sum(json('[7,1,9,null,5,3]'), 15, 16) ==> 56.0
160. avg()
json('[7,1,9,null,5,3]').avg() ==> 5.0
avg(json('[7,1,9,null,5,3]'), 15, 16) ==> 8.0
161. count()
json('[7,1,9,null,5,3]').count() ==> 5
count(json('[7,1,9,null,5,3]'), 15, 16) ==> 7
162. reverse()
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 ]
163. slice()
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 ]
164. sort()
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"
} ]
165. distinct()
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 ]
166. join()
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"
167. findByMax()
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
}
168. findByMin()
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
}
169. findByNullOrMax()
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
}
170. findByNullOrMin()
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
}
171. findByMaxOrNull()
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
}
172. findByMinOrNull()
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
}
Structural Functions
173. json()
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
}
}
174. entries()
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
}
} ]
175. keys()
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" ]
176. toArray()
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 ]
177. flatten()
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 ] ] ]
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 ] ]
flatten(json('[[[[1,2],[3,4]],[[5,6],[7,8]]],[[[9,10],[11,12]],[[13,14],[15,16]]]]'), 3)
==>
[ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 ]
178. map()
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 ]
}
}
179. field()
json('{"a":1,"b":[2,3],"c":{"d":4,"e":5}}').field(f:6,c:)
==>
{
"a" : 1,
"b" : [ 2, 3 ],
"f" : 6
}
180. coalesce()
json('["abc","",123,false,null]').coalesce('xyz') ==> [ "abc", "", 123, false, "xyz" ]
json('{"a":null,"c":"abc"}').coalesce(a,b,c,'xyz') ==> "abc"
181. csv()
json('{"len1":"12.3\\"","len2":"26.1\\"","len3":"64.0\\""}').csv() ==> "12.3""","26.1""","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
Jossons Basic
Jossons stores JSON datasets in a map of type Map<String, Josson>
for placeholder resolution.
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);
Jossons Template Language
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}}
Nested Placeholders
Placeholders can be nested and are resolved from inside to outside. Resolved one is replaced with text and continue for the next round.
Example:
{{stock->[itemCode='{{order->items[qrCode='{{qrCode}}'].itemCode}}'].qty}}
{{qrCode}}
is resolved to1234567890
{{order->items[qrCode='1234567890'].itemCode}}
is resolved toABCDE
{{stock->[itemCode='ABCDE'].qty}}
is resolved to100
Ternary Syntax
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?}}
Implicit Variables
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
Fill In
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.
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(##, $V...)]->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
Jossons Resolver
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 progress 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
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();
An Example of the progress output:
Round 1 : Resolving salesOrder from sales_order?{orderId:'202208172',orderSubs:{$elemMatch:{subsId:'S00000263'}}},{_id:0,'orderSubs.$':1}
Round 1 : Resolved salesOrder = Object with 18 elements
Round 1 : Resolving {offers=orderSub->offers, plan=subsDetail->planName, txnDate=salesOrder->orderPayment.txnDate.formatDate('yyyy-MM-dd'), txnId=salesOrder->orderPayment.paymentId, products=orderSub->offers.products.productName}
Round 1 : Resolved txnDate = "2021-08-18"
Round 1 : Resolved txnId = "1210818002190"
Round 2 : Resolving custSub from cust_subscription?{subsId:'S00000263'}
Round 2 : Resolved custSub = Object with 32 elements
Round 2 : Resolving {orderSub=salesOrder->orderSubs[subsId='S00000263'], subsDetail=custSub->subsDetails[deleteFlag!=true]*.findByMaxOrNull(startDate)}
Round 2 : Resolved orderSub = Object with 33 elements
Round 2 : Resolved subsDetail = Object with 21 elements
Round 3 : Resolving customer from ?{custId:'C00000045'}
Round 3 : Resolved customer = Object with 27 elements
Round 3 : Resolving {offers=orderSub->offers, plan=subsDetail->planName}
Round 3 : Resolved offers = Array with 2 elements
Round 3 : Resolved plan = "Promotion"
Round 3 : Resolved products = Array with 8 elements
Round 4 : End
Dictionary Finder
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 either:
-
A statement that represent a value.
"1" // IntNode "2.3" // DoubleNode "'Text'" // TextNode "true" // BooleanNode "null" // NullNode
-
A Jossons query that retrieve data from other datasets.
"otherKey->jossonQuery"
-
A database query statement, please refer to Data Finder.
"collectionName ? {findStatement}"
-
A join operation query to merge two datasets, please refer to Join Datasets.
"leftQuery{keyL1,keyL2...} <=< rightQuery{keyR1,keyR2...}"
Data Finder
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 ? {findStatement},{projectStatment}"
"collectionName ? [aggregateStatements]"
"? {findStatement}"
"? {findStatement},{projectStatment}"
"? [aggregateStatements]"
Many-documents query syntax that request for an ArrayNode
:
"collectionName[] ? {findStatement}"
"collectionName[] ? {findStatement},{projectStatment}"
"collectionName[] ? [aggregateStatements]"
"[] ? {findStatement}"
"[] ? {findStatement},{projectStatment}"
"[] ? [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.
Join Datasets
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.
There are two join types.
- Join One - Find the first matched object node and merge the object elements.
- Join Many - Find all matched nodes and embed into the object as an array node.
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.
For Join Many operations, the arrayName:
is optional. If arrayName
is not given, the last element name of the query is used.
-
Inner Join One
>=<
"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...}"
Examples:
"order->offers{offerId} <=< offers->map(offerId, offerName, offerDesc){offerId}"
"customers{custId} <=<< followups{custId}"
Appendix
MongoDB Adapter
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;
};
}
}