Java用 Rhino/Nashorn 代替第三方 JSON 转换库
Java本身就自带JS引擎,自从Java1.6开始就支持了,愈来愈好。我对js比较熟悉,因此有个大胆的想法,为什么不用自带js引擎作json转换呢?这样我们可以不用引入其他第三方库。
背景知识:Java6提供对执行脚本语言的支持,这个支持来自于JSR223规范,对应的包是javax.script。默认情况下,Java6只支持JavaScript脚本,它底层的实现是MozillaRhino,它是个纯Java的JavaScript实现。
除了OpenJDK不自带js引擎外,Sun/Oracle的都支持。所以完全可以这么来做。
我本人很早就这么做了。只是早期1.6/1.7的Rhino性能低下,但到了1.8性能已经不能同日而语了,——因为已经升级到Nashorn引擎了,一个非常快的js引擎实现。另外一点,之前写的代码十分累赘。尽管也重构了几次,但还是写不好。于是现欲改之,改成为一个稍“明快”的版本。请各位看官见下面代码,其作用就是将JSON字符串转换为Java的Map或者List。
importjava.util.List; importjava.util.Map; importjavax.script.ScriptEngine; importjavax.script.ScriptEngineManager; importjavax.script.ScriptException; /** *json转为java对象的工具类 * *@authorfrank * */ publicclassJSON{ /** *创建js引擎工厂,支持java6/7的rhino和java8的nashorn * *@returnjs引擎 */ publicstaticScriptEngineengineFatory(){ returnnewScriptEngineManager() .getEngineByName(System.getProperty("java.version").contains("1.8.")?"nashorn":"rhino"); } /** *JVM自带的JS引擎 */ privatefinalstaticScriptEngineengine=engineFatory(); /** *读取json里面的map * *@paramjs *JSON字符串 *@paramkey *JSONPath,可以带有aa.bb.cc *@returnMap对象 */ @SuppressWarnings("unchecked") publicstaticMapgetMap(Stringjs,Stringkey){ return(Map )accessMember(js,key,Map.class); } /** *读取json里面的map * *@paramjs *JSON字符串 *@returnMap对象 */ publicstaticMap getMap(Stringjs){ returngetMap(js,null); } /** *转换为map或list * *@paramjs *JSON字符串 *@paramkey *JSONPath,可以带有aa.bb.cc *@paramclazz *目标类型 *@return目标对象 */ @SuppressWarnings("unchecked") publicstatic TaccessMember(Stringjs,Stringkey,Class clazz){ Tresult=null; try{ engine.eval("varobj="+js);//rhino不能直接返回map,如eval("{a:1}") //-->null,必须加变量,例如执行varxx= //{...}; Objectobj; if(key==null){ obj=engine.eval("obj;"); }else{ if(key.contains(".")){ obj=engine.eval("obj."+key+";"); }else{ obj=engine.eval("obj['"+key+"'];"); } } result=(T)obj; }catch(ScriptExceptione){ System.err.println("脚本eval()运算发生异常!eval代码:"+js); e.printStackTrace(); } returnresult; } /** *读取json里面的list,list里面每一个都是map * *@paramjs *JSON字符串 *@paramkey *JSONPath,可以带有aa.bb.cc *@return包含Map的列表 */ @SuppressWarnings("unchecked") publicstaticList
其实使用起来非常地方便!js的对象本身是map结构,而Rhino原生对象NativeObject是js对象在Java语言里面的对应物,它已经实现了Map接口,所以完全可以把NativeObject当作map来使用!类型转换下即可!eval()返回的是object,如果可以判断object类型为NativeObject,直接转化(Map)object就可以了——接着就是使用get等方法,甚至在JSP页面中也可以使用。
List的也是同理。
下面是单测的代码。
importjava.util.List; importjava.util.Map; importorg.junit.Test; importcom.ajaxjs.util.json.JSON; importstaticorg.junit.Assert.*; publicclassTestJSON{ @Test publicvoidtestGetMap(){ Mapmap; map=JSON.getMap("{a:'hello',b:'world!',c:{d:'Nice!'}}"); System.out.println(map.get("a")); assertNotNull(map); map=JSON.getMap("{a:'hello',b:'world!',c:{d:'Nice!'}}","c"); System.out.println(map.get("d")); assertNotNull(map); map=JSON.getMap("{a:'hello',b:'world!',c:{d:'Nice!',e:{f:'fff'}}}","c.e"); System.out.println(map.get("f")); assertNotNull(map); } @Test publicvoidtestGetListMap(){ List >list; list=JSON.getList("[{a:'hello'},123,true]"); System.out.println(list.get(0).get("a")); assertTrue(list.size()>0); list=JSON.getList("[{a:'hello'},{b:'world!'},{c:{d:'Nice!'}}]"); System.out.println(list.get(0).get("a")); assertTrue(list.size()>0); list=JSON.getList("{a:'hello',b:'world!',c:[{d:'Nice!!!'}]}","c"); System.out.println(list.get(0).get("d")); } @Test publicvoidtestGetListString(){ List list; list=JSON.getStringList("['a','b','c']"); System.out.println(list.get(0)); assertTrue(list.size()>0); list=JSON.getStringList("[1,'b','c']"); System.out.println(list.get(1)); assertTrue(list.size()>0); } }
值得注意的是,虽然JSEngine提供了Map接口,但通常只能读的操作,如果对其执行map.put(key,value)的操作,是会引发UnsupportOperation的异常的。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。