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对象
*/
publicstaticMapgetMap(Stringjs){
returngetMap(js,null);
}
/**
*转换为map或list
*
*@paramjs
*JSON字符串
*@paramkey
*JSONPath,可以带有aa.bb.cc
*@paramclazz
*目标类型
*@return目标对象
*/
@SuppressWarnings("unchecked")
publicstaticTaccessMember(Stringjs,Stringkey,Classclazz){
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(){
Listlist;
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的异常的。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。