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

 更新时间:2017年05月08日 10:09:26   作者:sp42a  
本篇文章主要介绍了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 的异常的。

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

相关文章

  • Java C++题解leetcode1620网络信号最好的坐标

    Java C++题解leetcode1620网络信号最好的坐标

    这篇文章主要为大家介绍了Java C++题解leetcode1620网络信号最好的坐标示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-01-01
  • 基于JVM 中常见垃圾收集算法介绍

    基于JVM 中常见垃圾收集算法介绍

    下面小编就为大家分享一篇基于JVM 中常见垃圾收集算法介绍,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-01-01
  • spring-boot中的SPI机制实例讲解

    spring-boot中的SPI机制实例讲解

    这篇文章主要介绍了spring-boot中的SPI机制实例讲解,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-07-07
  • Java系统变量参数获取设置System.getProperties()的方法

    Java系统变量参数获取设置System.getProperties()的方法

    这篇文章主要介绍了Java系统变量参数System.getProperties()获取设置方法,文末给大家提到了Systm.getenv()与System.getProperties()区别,需要的朋友可以参考下
    2022-01-01
  • IDEA搭建SpringBoot多模块聚合工程过程详解(多模块聚合工程)

    IDEA搭建SpringBoot多模块聚合工程过程详解(多模块聚合工程)

    这篇文章主要是介绍一下,如何在IDEA开发工具下,搭建一个基于SpringBoot的多模块聚合工程项目,本篇文章,将项目模块细分为几个子工程模块,在文中给大家详细介绍过,对IDEA搭建SpringBoot多模块聚合工程感兴趣的朋友一起看看吧
    2022-04-04
  • Spring Data JPA的Audit功能审计数据库的变更

    Spring Data JPA的Audit功能审计数据库的变更

    数据库审计是指当数据库有记录变更时,可以记录数据库的变更时间和变更人等,这样以后出问题回溯问责也比较方便,本文讨论Spring Data JPA审计数据库变更问题,感兴趣的朋友一起看看吧
    2021-06-06
  • mybatis plus saveBatch方法方法执行慢导致接口发送慢解决分析

    mybatis plus saveBatch方法方法执行慢导致接口发送慢解决分析

    这篇文章主要为大家介绍了mybatis plus saveBatch方法导致接口发送慢解决分析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-10-10
  • 详解Java实现简单SPI流程

    详解Java实现简单SPI流程

    这篇文章主要介绍了Java实现简单SPI流程,SPI英文全称为Service Provider Interface,顾名思义,服务提供者接口,它是jdk提供给“服务提供厂商”或者“插件开发者”使用的接口
    2023-03-03
  • SpringBoot 利用thymeleaf自定义错误页面

    SpringBoot 利用thymeleaf自定义错误页面

    这篇文章主要介绍了SpringBoot 利用thymeleaf自定义错误页面,帮助大家更好的理解和使用springboot 框架,感兴趣的朋友可以了解下
    2020-11-11
  • Spring用AspectJ开发AOP(基于Annotation)

    Spring用AspectJ开发AOP(基于Annotation)

    这篇文章主要介绍了Spring用AspectJ开发AOP(基于Annotation),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-10-10

最新评论