Java用 Rhino/Nashorn 代替第三方 JSON 轉(zhuǎn)換庫
Java 本身就自帶 JS 引擎,自從 Java 1.6 開始就支持了,愈來愈好。我對 js 比較熟悉,因此有個大膽的想法,為什么不用自帶 js 引擎作 json 轉(zhuǎn)換呢?這樣我們可以不用引入其他第三方庫。
背景知識:Java 6 提供對執(zhí)行腳本語言的支持,這個支持來自于 JSR223 規(guī)范,對應(yīng)的包是 javax.script。默認(rèn)情況下,Java 6 只支持 JavaScript 腳本,它底層的實(shí)現(xiàn)是 Mozilla Rhino,它是個純 Java 的 JavaScript 實(shí)現(xiàn)。
除了 OpenJDK 不自帶 js 引擎外,Sun/Oracle 的都支持。所以完全可以這么來做。
我本人很早就這么做了。只是早期 1.6/1.7 的 Rhino 性能低下,但到了 1.8 性能已經(jīng)不能同日而語了,——因為已經(jīng)升級到 Nashorn 引擎了,一個非??斓?js 引擎實(shí)現(xiàn)。另外一點(diǎn),之前寫的代碼十分累贅。盡管也重構(gòu)了幾次,但還是寫不好。于是現(xiàn)欲改之,改成為一個稍“明快”的版本。請各位看官見下面代碼,其作用就是將 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 對象的工具類
*
* @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 對象
*/
@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);
}
/**
* 轉(zhuǎn)換為 map 或 list
*
* @param js
* JSON 字符串
* @param key
* JSON Path,可以帶有 aa.bb.cc
* @param clazz
* 目標(biāo)類型
* @return 目標(biāo)對象
*/
@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 里面每一個都是 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 里面使用不方便,將其轉(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)用這個方法轉(zhuǎn)換到 int");
return 0;
} else {
return d.intValue();
}
}
}
其實(shí)使用起來非常地方便!js 的對象本身是 map 結(jié)構(gòu),而 Rhino 原生對象 NativeObject 是 js 對象在 Java 語言里面的對應(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 的也是同理。
下面是單測的代碼。
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 接口,但通常只能讀的操作,如果對其執(zhí)行 map.put(key, value) 的操作,是會引發(fā) UnsupportOperation 的異常的。
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
SpringBoot定時任務(wù)參數(shù)運(yùn)行代碼實(shí)例解析
這篇文章主要介紹了SpringBoot定時任務(wù)運(yùn)行代碼實(shí)例解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-06-06
java 中 阻塞隊列BlockingQueue詳解及實(shí)例
這篇文章主要介紹了java 中 阻塞隊列BlockingQueue詳解及實(shí)例的相關(guān)資料,需要的朋友可以參考下2017-03-03
JAVA發(fā)送HTTP請求的多種方式詳細(xì)總結(jié)
目前做項目中有一個需求是這樣的,需要通過Java發(fā)送url請求,查看該url是否有效,這時我們可以通過獲取狀態(tài)碼來判斷,下面這篇文章主要給大家介紹了關(guān)于JAVA發(fā)送HTTP請求的多種方式總結(jié)的相關(guān)資料,需要的朋友可以參考下2023-01-01
java教學(xué)筆記之對象的創(chuàng)建與銷毀
面向?qū)ο蟮木幊陶Z言使程序能夠直觀的反應(yīng)客觀世界的本來面目,并且使軟件開發(fā)人員能夠運(yùn)用人類認(rèn)識事物所采用的一般思維方法進(jìn)行軟件開發(fā),是當(dāng)今計算機(jī)領(lǐng)域中軟件開發(fā)和應(yīng)用的主流技術(shù)。2016-01-01

