Java用 Rhino/Nashorn 代替第三方 JSON 转换库
java 本身就自带 js 引擎,自从 java 1.6 开始就支持了,愈来愈好。我对 js 比较熟悉,因此有个大胆的想法,为什么不用自带 js 引擎作 json 转换呢?这样我们可以不用引入其他第三方库。
背景知识:java 6 提供对执行脚本语言的支持,这个支持来自于 jsr223 规范,对应的包是 javax.script。默认情况下,java 6 只支持 javascript 脚本,它底层的实现是 mozilla rhino,它是个纯 java 的 javascript 实现。
除了 openjdk 不自带 js 引擎外,sun/oracle 的都支持。所以完全可以这么来做。
我本人很早就这么做了。只是早期 1.6/1.7 的 rhino 性能低下,但到了 1.8 性能已经不能同日而语了,——因为已经升级到 nashorn 引擎了,一个非常快的 js 引擎实现。另外一点,之前写的代码十分累赘。尽管也重构了几次,但还是写不好。于是现欲改之,改成为一个稍“明快”的版本。请各位看官见下面代码,其作用就是将 json 字符串转换为 java 的 map 或者 list。
import java.util.list; import java.util.map; import javax.script.scriptengine; import javax.script.scriptenginemanager; import javax.script.scriptexception; /** * json 转为 java 对象的工具类 * * @author frank * */ public class json { /** * 创建 js 引擎工厂,支持 java 6/7 的 rhino 和 java 8 的 nashorn * * @return js 引擎 */ public static scriptengine enginefatory() { return new scriptenginemanager() .getenginebyname(system.getproperty("java.version").contains("1.8.") ? "nashorn" : "rhino"); } /** * jvm 自带的 js 引擎 */ private final static scriptengine engine = enginefatory(); /** * 读取 json 里面的 map * * @param js * json 字符串 * @param key * json path,可以带有 aa.bb.cc * @return map 对象 */ @suppresswarnings("unchecked") public static map<string, object> getmap(string js, string key) { return (map<string, object>) accessmember(js, key, map.class); } /** * 读取 json 里面的 map * * @param js * json 字符串 * @return map 对象 */ public static map<string, object> getmap(string js) { return getmap(js, null); } /** * 转换为 map 或 list * * @param js * json 字符串 * @param key * json path,可以带有 aa.bb.cc * @param clazz * 目标类型 * @return 目标对象 */ @suppresswarnings("unchecked") public static <t> t accessmember(string js, string key, class<t> clazz) { t result = null; try { engine.eval("var obj = " + js);// rhino 不能直接返回 map,如 eval("{a:1}") // -->null,必须加变量,例如 执行 var xx = // {...}; object obj; 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 (scriptexception e) { system.err.println("脚本eval()运算发生异常!eval 代码:" + js); e.printstacktrace(); } return result; } /** * 读取 json 里面的 list,list 里面每一个都是 map * * @param js * json 字符串 * @param key * json path,可以带有 aa.bb.cc * @return 包含 map 的列表 */ @suppresswarnings("unchecked") public static list<map<string, object>> getlist(string js, string key) { return (list<map<string, object>>) accessmember(js, key, list.class); } /** * 读取 json 里面的 list,list 里面每一个都是 map * * @param js * json 字符串 * @return 包含 map 的列表 */ public static list<map<string, object>> getlist(string js) { return getlist(js, null); } /** * 读取 json 里面的 list,list 里面每一个都是 string * * @param js * json 字符串 * @param key * json path,可以带有 aa.bb.cc * @return 包含 string 的列表 */ @suppresswarnings("unchecked") public static list<string> getstringlist(string js, string key) { return (list<string>) accessmember(js, key, list.class); } /** * 读取 json 里面的 list,list 里面每一个都是 string * * @param js * json 字符串 * @return 包含 string 的列表 */ public static list<string> getstringlist(string js) { return getstringlist(js, null); } /** * js number 为 double 类型,在 java 里面使用不方便,将其转换为 int * * @param d * js number * @return int 值 */ public static int double2int(double d) { if (d > integer.max_value) { system.out.println(d + "数值太大,不应用这个方法转换到 int"); return 0; } else { return d.intvalue(); } } }
其实使用起来非常地方便!js 的对象本身是 map 结构,而 rhino 原生对象 nativeobject 是 js 对象在 java 语言里面的对应物,它已经实现了 map 接口,所以完全可以把 nativeobject 当作 map 来使用!类型转换下即可!eval() 返回的是 object,如果可以判断 object 类型为 nativeobject,直接转化 (map)object 就可以了——接着就是使用 get 等方法,甚至在 jsp 页面中也可以使用。
list 的也是同理。
下面是单测的代码。
import java.util.list; import java.util.map; import org.junit.test; import com.ajaxjs.util.json.json; import static org.junit.assert.*; public class testjson { @test public void testgetmap() { map<string, object> map; 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 public void testgetlistmap() { list<map<string, object>> 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 public void testgetliststring() { list<string> 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 的异常的。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。