服务器之家

服务器之家 > 正文

Java用 Rhino/Nashorn 代替第三方 JSON 转换库

时间:2020-09-25 15:16     来源/作者:sp42a

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。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
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 的也是同理。

下面是单测的代码。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
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 的异常的。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。

原文链接:http://blog.csdn.net/zhangxin09/article/details/51810804

标签:

相关文章

热门资讯

2020微信伤感网名听哭了 让对方看到心疼的伤感网名大全
2020微信伤感网名听哭了 让对方看到心疼的伤感网名大全 2019-12-26
Intellij idea2020永久破解,亲测可用!!!
Intellij idea2020永久破解,亲测可用!!! 2020-07-29
歪歪漫画vip账号共享2020_yy漫画免费账号密码共享
歪歪漫画vip账号共享2020_yy漫画免费账号密码共享 2020-04-07
电视剧《琉璃》全集在线观看 琉璃美人煞1-59集免费观看地址
电视剧《琉璃》全集在线观看 琉璃美人煞1-59集免费观看地址 2020-08-12
最新idea2020注册码永久激活(激活到2100年)
最新idea2020注册码永久激活(激活到2100年) 2020-07-29
返回顶部