Java用 Rhino/Nashorn 代替第三方 JSON 轉(zhuǎn)換庫(kù)
Java 本身就自帶 JS 引擎,自從 Java 1.6 開始就支持了,愈來愈好。我對(duì) js 比較熟悉,因此有個(gè)大膽的想法,為什么不用自帶 js 引擎作 json 轉(zhuǎn)換呢?這樣我們可以不用引入其他第三方庫(kù)。
背景知識(shí):Java 6 提供對(duì)執(zhí)行腳本語言的支持,這個(gè)支持來自于 JSR223 規(guī)范,對(duì)應(yīng)的包是 javax.script。默認(rèn)情況下,Java 6 只支持 JavaScript 腳本,它底層的實(shí)現(xiàn)是 Mozilla Rhino,它是個(gè)純 Java 的 JavaScript 實(shí)現(xiàn)。
除了 OpenJDK 不自帶 js 引擎外,Sun/Oracle 的都支持。所以完全可以這么來做。
我本人很早就這么做了。只是早期 1.6/1.7 的 Rhino 性能低下,但到了 1.8 性能已經(jīng)不能同日而語了,——因?yàn)橐呀?jīng)升級(jí)到 Nashorn 引擎了,一個(gè)非??斓?js 引擎實(shí)現(xiàn)。另外一點(diǎn),之前寫的代碼十分累贅。盡管也重構(gòu)了幾次,但還是寫不好。于是現(xiàn)欲改之,改成為一個(gè)稍“明快”的版本。請(qǐng)各位看官見下面代碼,其作用就是將 JSON 字符串轉(zhuǎn)換為 Java 的 Map 或者 List。
import java.util.List; import java.util.Map; import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; import javax.script.ScriptException; /** * json 轉(zhuǎn)為 java 對(duì)象的工具類 * * @author frank * */ public class JSON { /** * 創(chuàng)建 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 對(duì)象 */ @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 對(duì)象 */ public static Map<String, Object> getMap(String js) { return getMap(js, null); } /** * 轉(zhuǎn)換為 map 或 list * * @param js * JSON 字符串 * @param key * JSON Path,可以帶有 aa.bb.cc * @param clazz * 目標(biāo)類型 * @return 目標(biāo)對(duì)象 */ @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,必須加變量,例如 執(zhí)行 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()運(yùn)算發(fā)生異常!eval 代碼:" + js); e.printStackTrace(); } return result; } /** * 讀取 json 里面的 list,list 里面每一個(gè)都是 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 里面每一個(gè)都是 map * * @param js * JSON 字符串 * @return 包含 Map 的列表 */ public static List<Map<String, Object>> getList(String js) { return getList(js, null); } /** * 讀取 json 里面的 list,list 里面每一個(gè)都是 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 里面每一個(gè)都是 String * * @param js * JSON 字符串 * @return 包含 String 的列表 */ public static List<String> getStringList(String js) { return getStringList(js, null); } /** * js number 為 double 類型,在 java 里面使用不方便,將其轉(zhuǎn)換為 int * * @param d * js number * @return int 值 */ public static int double2int(Double d) { if (d > Integer.MAX_VALUE) { System.out.println(d + "數(shù)值太大,不應(yīng)用這個(gè)方法轉(zhuǎn)換到 int"); return 0; } else { return d.intValue(); } } }
其實(shí)使用起來非常地方便!js 的對(duì)象本身是 map 結(jié)構(gòu),而 Rhino 原生對(duì)象 NativeObject 是 js 對(duì)象在 Java 語言里面的對(duì)應(yīng)物,它已經(jīng)實(shí)現(xiàn)了 Map 接口,所以完全可以把 NativeObject 當(dāng)作 map 來使用!類型轉(zhuǎn)換下即可!eval() 返回的是 object,如果可以判斷 object 類型為 NativeObject,直接轉(zhuǎn)化 (Map)object 就可以了——接著就是使用 get 等方法,甚至在 JSP 頁面中也可以使用。
List 的也是同理。
下面是單測(cè)的代碼。
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 接口,但通常只能讀的操作,如果對(duì)其執(zhí)行 map.put(key, value) 的操作,是會(huì)引發(fā) UnsupportOperation 的異常的。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
SpringBoot參數(shù)校驗(yàn)之@Validated的使用詳解
這篇文章主要通過示例為大家詳細(xì)介紹一下介紹了SpringBoot參數(shù)校驗(yàn)中@Validated的使用方法,文中的示例代碼講解詳細(xì),需要的可以參考一下2022-06-06MyBatis框架關(guān)聯(lián)映射實(shí)例詳解
這篇文章主要介紹了MyBatis框架關(guān)聯(lián)映射,關(guān)系映射主要處理復(fù)雜的SQl查詢,如子查詢,多表聯(lián)查等復(fù)雜查詢,應(yīng)用此種需求時(shí)可以考慮使用,需要的朋友可以參考下2022-11-11Java ArrayList.toArray(T[]) 方法的參數(shù)類型是 T 而不是 E的原因分析
這篇文章主要介紹了Java ArrayList.toArray(T[]) 方法的參數(shù)類型是 T 而不是 E的原因分析的相關(guān)資料,需要的朋友可以參考下2016-04-04Mapper批量插入Oracle數(shù)據(jù)@InsertProvider注解
今天小編就為大家分享一篇關(guān)于Mapper批量插入Oracle數(shù)據(jù)@InsertProvider注解,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧2019-03-03在攔截器中讀取request參數(shù),解決在controller中無法二次讀取的問題
這篇文章主要介紹了在攔截器中讀取request參數(shù),解決在controller中無法二次讀取的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-10-10