pom
<dependency>
<groupId>com.googlecode.aviator</groupId>
<artifactId>aviator</artifactId>
<version>4.2.10</version>
</dependency>
<dependency>
<groupId>org.mvel</groupId>
<artifactId>mvel2</artifactId>
<version>2.4.10.Final</version>
</dependency>
對單一map的簡單操作
import java.util.HashMap; import java.util.Map; import com.googlecode.aviator.AviatorEvaluator; import com.googlecode.aviator.runtime.function.AbstractFunction; import com.googlecode.aviator.runtime.function.FunctionUtils; import com.googlecode.aviator.runtime.type.AviatorBigInt; import com.googlecode.aviator.runtime.type.AviatorObject; public class App { static{ //注冊函數 AviatorEvaluator.addFunction(new MinFunction()); } public static void main( String[] args ) { Map<String, Object> sourceMapItem = new HashMap<String, Object>(); sourceMapItem.put("pt", "mi"); sourceMapItem.put("ab", "abc"); sourceMapItem.put("xy", "xyz"); sourceMapItem.put("int1", 135); sourceMapItem.put("int2", 68); sourceMapItem.put("decimal2", 19.83); sourceMapItem.put("mydate", "2020-10-10 28:35:00"); sourceMapItem.put("value", 999); String expStr = "pt == 'mi'"; Object result; result = AviatorEvaluator.execute(expStr, sourceMapItem); System.out.println(expStr+" --- "+result); //邏輯與 + 簡單表達式 expStr = "pt == 'mi' && ab == 'abc' && int1 > 100 && decimal2 < 20.50"; result = AviatorEvaluator.execute(expStr, sourceMapItem); System.out.println(expStr+" --- "+result); //邏輯與或組合 expStr = "pt == 'mi1' || (decimal2 < 20.50 && xy+'X' == 'xyzX')"; result = AviatorEvaluator.execute(expStr, sourceMapItem); System.out.println(expStr+" --- "+result); //三目運算 expStr = "pt == 'mi1' ? ab == 'ab' : ab == 'abc'"; result = AviatorEvaluator.execute(expStr, sourceMapItem); System.out.println(expStr+" --- "+result); //函數 expStr = "pt != 'mi1' && string.length(xy) >= 5 "; result = AviatorEvaluator.execute(expStr, sourceMapItem); System.out.println(expStr+" --- "+result); //自定義函數 expStr = "pt == 'mi1' || myMin(int2 , int1) > 10 "; result = AviatorEvaluator.execute(expStr, sourceMapItem); System.out.println(expStr+" --- "+result); } static class MinFunction extends AbstractFunction { @Override public AviatorObject call(Map<String, Object> env, AviatorObject arg1, AviatorObject arg2) { Number left = FunctionUtils.getNumberValue(arg1, env); Number right = FunctionUtils.getNumberValue(arg2, env); return new AviatorBigInt(Math.min(left.doubleValue(), right.doubleValue())); } public String getName() { return "myMin"; } } }
輸出結果
pt == 'mi' --- true
pt == 'mi' && ab == 'abc' && int1 > 100 && decimal2 < 20.50 --- true pt == 'mi1' || (decimal2 < 20.50 && xy+'X' == 'xyzX') --- true pt == 'mi1' ? ab == 'ab' : ab == 'abc' --- true pt != 'mi1' && string.length(xy) >= 5 --- false pt == 'mi1' || myMin(int2 , int1) > 10 --- true
一個擴展的任務,Aviator對嵌套map進行stream過濾
import java.util.HashMap; import java.util.Map; import java.util.Optional; import java.util.stream.Collectors; import com.googlecode.aviator.AviatorEvaluator; public class MapForFilter { final static String EXPSTR = "(pt == 'mi' && ab == 'abc' && int1 > 100) || " + "(decimal2 < 20.50 && string.length(xy) >= 5)"; public static void main( String[] args ) { aviatorEvaluatorDemo(); } private static void aviatorEvaluatorDemo() { Map<String, Map<String, Object>> sourceMap = fillData(); Map<String, Map<String, Object>> resultdMap = parseMapForFilter(sourceMap,EXPSTR); System.out.println("過濾后:"+resultdMap); } private static Map<String, Map<String, Object>> fillData() { Map<String, Map<String, Object>> sourceMap = new HashMap<String, Map<String, Object>>(); Map<String, Object> sourceMapItem1 = new HashMap<String, Object>(); sourceMapItem1.put("pt", "mi"); sourceMapItem1.put("ab", "abc"); sourceMapItem1.put("xy", "xyz"); sourceMapItem1.put("int1", 135); sourceMapItem1.put("int2", 68); sourceMapItem1.put("decimal2", 19.83); sourceMapItem1.put("mydate", "2020-10-10 28:35:00"); sourceMapItem1.put("value", 999); Map<String, Object> sourceMapItem2 = new HashMap<String, Object>(); sourceMapItem2.put("pt", "oppo"); sourceMapItem2.put("ab", "ab"); sourceMapItem2.put("xy", "xy"); sourceMapItem2.put("int1", 24); sourceMapItem2.put("int2", 56); sourceMapItem2.put("decimal2", 519.83); sourceMapItem2.put("mydate", "2020-10-11 28:35:00"); sourceMapItem2.put("value", 888); Map<String, Object> sourceMapItem3 = new HashMap<String, Object>(); sourceMapItem3.put("pt", "oppo"); sourceMapItem3.put("ab", "cde"); sourceMapItem3.put("xy", "xy"); sourceMapItem3.put("int1", 213); sourceMapItem3.put("int2", 473); sourceMapItem3.put("decimal2", 119.83); sourceMapItem3.put("mydate", "2020-10-12 28:35:00"); sourceMapItem3.put("value", 777); Map<String, Object> sourceMapItem4 = new HashMap<String, Object>(); sourceMapItem4.put("pt", "oppo"); sourceMapItem4.put("ab", "fgh"); sourceMapItem4.put("xy", "xyz123"); sourceMapItem4.put("int1", 156); sourceMapItem4.put("int2", 215); sourceMapItem4.put("decimal2", 15.92); sourceMapItem4.put("mydate", "2020-10-13 28:35:00"); sourceMapItem4.put("value", 666); sourceMap.put("2020-10-10",sourceMapItem1); sourceMap.put("2020-10-11",sourceMapItem2); sourceMap.put("2020-10-12",sourceMapItem3); sourceMap.put("2020-10-13",sourceMapItem4); return sourceMap; } public static Map<String, Map<String, Object>> parseMapForFilter(Map<String, Map<String, Object>> sourceMap,String exp) { return Optional.ofNullable(sourceMap).map( (v) -> { Map<String, Map<String, Object>> params = v.entrySet().stream() .filter(map -> checkItem(map.getValue(),exp)) .collect(Collectors.toMap(map -> map.getKey(), map -> map.getValue())); return params; } ).orElse(null); } private static boolean checkItem(Map<String, Object> itemMap,String exp) { Object result = AviatorEvaluator.execute(exp, itemMap); return (boolean) result; } }
MVEL的實現同樣的過濾任務
為了適應MVEL,注意表達式的字符有少許不同。
import java.util.HashMap; import java.util.Map; import java.util.Optional; import java.util.stream.Collectors; import org.mvel2.MVEL; public class MapForFilterMvel { final static String EXPSTR = "(m.pt == 'mi' && m.ab == 'abc' && m.int1 > 100) || " + "(m.decimal2 < 20.50 && m.xy.length()>5)"; public static void main( String[] args ) { mvelDemo(); } private static void mvelDemo() { Map<String, Map<String, Object>> sourceMap = fillData(); Map<String, Map<String, Object>> resultdMap = parseMapForMvelFilter(sourceMap); System.out.println("過濾后:"+resultdMap); } private static Map<String, Map<String, Object>> parseMapForMvelFilter(Map<String, Map<String, Object>> sourceMap) { return Optional.ofNullable(sourceMap).map( (v) -> { Map<String, Map<String, Object>> params = v.entrySet().stream() .filter(map -> checkItemByMvel(map.getValue())) .collect(Collectors.toMap(map -> map.getKey(), map -> map.getValue())); return params; } ).orElse(null); } private static boolean checkItemByMvel(Map<String, Object> value) { Map<String, Object> paramMap = new HashMap<String, Object>(); paramMap.put("m", value); Object object = MVEL.eval(EXPSTR, paramMap); return (boolean)object; } private static Map<String, Map<String, Object>> fillData() { Map<String, Map<String, Object>> sourceMap = new HashMap<String, Map<String, Object>>(); Map<String, Object> sourceMapItem1 = new HashMap<String, Object>(); sourceMapItem1.put("pt", "mi"); sourceMapItem1.put("ab", "abc"); sourceMapItem1.put("xy", "xyz"); sourceMapItem1.put("int1", 135); sourceMapItem1.put("int2", 68); sourceMapItem1.put("decimal2", 19.83); sourceMapItem1.put("mydate", "2020-10-10 28:35:00"); sourceMapItem1.put("value", 999); Map<String, Object> sourceMapItem2 = new HashMap<String, Object>(); sourceMapItem2.put("pt", "oppo"); sourceMapItem2.put("ab", "ab"); sourceMapItem2.put("xy", "xy"); sourceMapItem2.put("int1", 24); sourceMapItem2.put("int2", 56); sourceMapItem2.put("decimal2", 519.83); sourceMapItem2.put("mydate", "2020-10-11 28:35:00"); sourceMapItem2.put("value", 888); Map<String, Object> sourceMapItem3 = new HashMap<String, Object>(); sourceMapItem3.put("pt", "oppo"); sourceMapItem3.put("ab", "cde"); sourceMapItem3.put("xy", "xy"); sourceMapItem3.put("int1", 213); sourceMapItem3.put("int2", 473); sourceMapItem3.put("decimal2", 119.83); sourceMapItem3.put("mydate", "2020-10-12 28:35:00"); sourceMapItem3.put("value", 777); Map<String, Object> sourceMapItem4 = new HashMap<String, Object>(); sourceMapItem4.put("pt", "oppo"); sourceMapItem4.put("ab", "fgh"); sourceMapItem4.put("xy", "xyz123"); sourceMapItem4.put("int1", 156); sourceMapItem4.put("int2", 215); sourceMapItem4.put("decimal2", 15.92); sourceMapItem4.put("mydate", "2020-10-13 28:35:00"); sourceMapItem4.put("value", 666); sourceMap.put("2020-10-10",sourceMapItem1); sourceMap.put("2020-10-11",sourceMapItem2); sourceMap.put("2020-10-12",sourceMapItem3); sourceMap.put("2020-10-13",sourceMapItem4); return sourceMap; } }
對map集合的過濾:結果:
過濾后:
{
2020-10-10={ab=abc, xy=xyz, int2=68, mydate=2020-10-10 28:35:00, int1=135, pt=mi, decimal2=19.83, value=999},
2020-10-13={ab=fgh, xy=xyz123, int2=215, mydate=2020-10-13 28:35:00, int1=156, pt=oppo, decimal2=15.92, value=666}
}
Aviator的優缺點
final static String EXPSTR = "(pt == 'mi' && ab == 'abc' && int1 > 100) || (decimal2 < 20.50 && string.length(xy) >= 5)";
- 更輕量級。
- 對map的支持非常好,可以說就是為了Map量身定做的,對key變量直接使用就可以了。
- 函數只能使用內置函數或者自定義函數。
- 不支持對象的屬性和方法。
- 不支持if else和for循環。
MVEL的優點
final static String EXPSTR = "(m.pt == 'mi' && m.ab == 'abc' && m.int1 > 100) || (m.decimal2 < 20.50 && m.xy.length()>5)";
- 功能更加強大。
- 使用map需要再包一層,用於指示map的根。稍微有些限制,但也不影響使用,固定一個map變量名就好了。
- 支持對象屬性、方法,這就厲害了,可以復用全部的java內置class,也可以使用你自己寫的class的屬性和方法。也可以自定義方法。
- 支持對象new操作,支持斷言,支持對象賦值。
- 支持if else流程,支持循環。
操作符列表
| 序號 | 操作符 | 結合性 | 操作數限制 |
|---|---|---|---|
| 0 | () [ ] | 從左到右 | ()用於函數調用,[ ]用於數組和java.util.List的元素訪問,要求[indx]中的index必須為整型 |
| 1 | ! - ~ | 從右到左 | ! 能用於Boolean,- 僅能用於Number,~僅能用於整數 |
| 2 | * / % | 從左到右 | Number之間 |
| 3 | + - | 從左到右 | + - 都能用於Number之間, + 還能用於String之間,或者String和其他對象 |
| 4 | << >> >>> | 從左到右 | 僅能用於整數 |
| 5 | < <= > >= | 從左到右 | Number之間、String之間、Pattern之間、變量之間、其他類型與nil之間 |
| 6 | == != =~ | 從左到右 | ==和!=作用於Number之間、String之間、Pattern之間、變量之間、其他類型與nil之間以及String和java.util.Date之間,=~僅能作用於String和Pattern之間 |
| 7 | & | 從左到右 | 整數之間 |
| 8 | ^ | 從左到右 | 整數之間 |
| 9 | ¦ | 從左到右 | 整數之間 |
| 10 | && | 從左到右 | Boolean之間,短路 |
| 11 | ¦¦ | 從左到右 | Boolean之間,短路 |
| 12 | ? : | 從右到左 | 第一個操作數的結果必須為Boolean,第二和第三操作數結果無限制 |
內置函數
| 函數名稱 | 說明 |
|---|---|
| sysdate() | 返回當前日期對象java.util.Date |
| rand() | 返回一個介於0-1的隨機數,double類型 |
| print([out],obj) | 打印對象,如果指定out,向out打印,否則輸出到控制台 |
| println([out],obj) | 與print類似,但是在輸出后換行 |
| now() | 返回System.currentTimeMillis |
| long(v) | 將值的類型轉為long |
| double(v) | 將值的類型轉為double |
| str(v) | 將值的類型轉為string |
| date_to_string(date,format) | 將Date對象轉化化特定格式的字符串,2.1.1新增 |
| string_to_date(source,format) | 將特定格式的字符串轉化為Date對象,2.1.1新增 |
| string.contains(s1,s2) | 判斷s1是否包含s2,返回Boolean |
| string.length(s) | 求字符串長度,返回Long |
| string.startsWith(s1,s2) | s1是否以s2開始,返回Boolean |
| string.endsWith(s1,s2) | s1是否以s2結尾,返回Boolean |
| string.substring(s,begin[,end]) | 截取字符串s,從begin到end,end如果忽略的話,將從begin到結尾,與java.util.String.substring一樣。 |
| string.indexOf(s1,s2) | java中的s1.indexOf(s2),求s2在s1中的起始索引位置,如果不存在為-1 |
| string.split(target,regex,[limit]) | Java里的String.split方法一致,2.1.1新增函數 |
| string.join(seq,seperator) | 將集合seq里的元素以seperator為間隔連接起來形成字符串,2.1.1新增函數 |
| string.replace_first(s,regex,replacement) | Java里的String.replaceFirst 方法,2.1.1新增 |
| string.replace_all(s,regex,replacement) | Java里的String.replaceAll方法 ,2.1.1新增 |
| math.abs(d) | 求d的絕對值 |
| math.sqrt(d) | 求d的平方根 |
| math.pow(d1,d2) | 求d1的d2次方 |
| math.log(d) | 求d的自然對數 |
| math.log10(d) | 求d以10為底的對數 |
| math.sin(d) | 正弦函數 |
| math.cos(d) | 余弦函數 |
| math.tan(d) | 正切函數 |
| map(seq,fun) | 將函數fun作用到集合seq每個元素上,返回新元素組成的集合 |
| filter(seq,predicate) | 將謂詞predicate作用在集合的每個元素上,返回謂詞為true的元素組成的集合 |
| count(seq) | 返回集合大小 |
| include(seq,element) | 判斷element是否在集合seq中,返回boolean值 |
| sort(seq) | 排序集合,僅對數組和List有效,返回排序后的新集合 |
| reduce(seq,fun,init) | fun接收兩個參數,第一個是集合元素,第二個是累積的函數,本函數用於將fun作用在集合每個元素和初始值上面,返回最終的init值 |
| seq.eq(value) | 返回一個謂詞,用來判斷傳入的參數是否跟value相等,用於filter函數,如filter(seq,seq.eq(3)) 過濾返回等於3的元素組成的集合 |
| seq.neq(value) | 與seq.eq類似,返回判斷不等於的謂詞 |
| seq.gt(value) | 返回判斷大於value的謂詞 |
| seq.ge(value) | 返回判斷大於等於value的謂詞 |
| seq.lt(value) | 返回判斷小於value的謂詞 |
| seq.le(value) | 返回判斷小於等於value的謂詞 |
| seq.nil() | 返回判斷是否為nil的謂詞 |
| seq.exists() | 返回判斷不為nil的謂詞 |
常量和變量
| 值 | 說明 |
|---|---|
| true | 真值 |
| false | 假值 |
| nil | 空值 |
$digit |
正則表達式匹配成功后的分組,$0表示匹配的字符串,$1表示第一個分組 etc. |
參考資料
5.0版以后成為了aviatorscript
https://github.com/killme2008/aviatorscript
