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

作者:sp42a 时间:2023-11-04 02:20:26 

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 的异常的。

来源:http://blog.csdn.net/zhangxin09/article/details/51810804

标签:rhino,nashorn
0
投稿

猜你喜欢

  • c# 实现获取汉字十六进制Unicode编码字符串的实例

    2023-03-21 11:22:35
  • Java窗口精细全方位讲解

    2023-03-05 15:35:15
  • mybatis if标签判断不生效的解决方法

    2023-11-28 12:30:49
  • C++编写实现图书管理系统

    2022-03-05 09:51:00
  • Spring Boot 利用 XML 方式整合 MyBatis

    2023-02-09 23:01:18
  • Android 设置Edittext获取焦点并弹出软键盘

    2022-02-28 12:02:02
  • 简单介绍java中equals以及==的用法

    2023-01-28 07:47:41
  • Android如何通过组合的方式自定义View

    2022-11-01 04:33:46
  • C#中Array与ArrayList用法及转换的方法

    2021-07-18 13:13:23
  • 浅谈Hibernate对象状态之间的神奇转换

    2021-12-18 02:13:18
  • 深入理解C#窗体关闭事件

    2023-06-01 14:38:56
  • Mybatis下的SQL注入漏洞原理及防护方法解析

    2022-06-30 18:38:29
  • Android三级缓存原理讲解

    2023-06-15 19:35:05
  • java日期工具类实例分享

    2023-05-17 17:50:40
  • 浅谈java中String与StringBuffer的不同

    2021-11-24 11:54:12
  • mybatis中使用InsertProvider注解报错解决全过程

    2021-06-25 18:36:42
  • Python和Java的语法对比分析语法简洁上python的确完美胜出

    2023-08-10 17:06:54
  • springboot项目中jackson-序列化-处理 NULL教程

    2022-11-03 14:36:21
  • c# wpf如何使用Blend工具绘制Control样式

    2022-10-26 09:35:25
  • 详解SpringBoot中的统一功能处理的实现

    2022-07-06 12:14:49
  • asp之家 软件编程 m.aspxhome.com