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

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

(0)

相关推荐

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

    Java 本身就自带 JS 引擎,自从 Java 1.6 开始就支持了,愈来愈好.我对 js 比较熟悉,因此有个大胆的想法,为什么不用自带 js 引擎作 json 转换呢?这样我们可以不用引入其他第三方库. 背景知识:Java 6 提供对执行脚本语言的支持,这个支持来自于 JSR223 规范,对应的包是 javax.script.默认情况下,Java 6 只支持 JavaScript 脚本,它底层的实现是 Mozilla Rhino,它是个纯 Java 的 JavaScript 实现. 除了 O

  • Java的JSON转换库GSON的基本使用方法示例

    下载和部署GSON GSON的GitHub页面地址:https://github.com/google/gson 在使用GSON API工作之前,你需要下载库(jar文件),并将其包含到类路径中.库,连同源代码和Java文档,都可以从http://code.google.com/p/google-gson/downloads/list下载.下载完毕后,添加gson-<version>.jar到类路径.对于那些偏好使用Maven管理依赖(JAR文件)的读者,添加如下依赖到pom.xml. <

  • Java字节与字符流永久存储json数据

    使用第三方json转换工具,阿里巴巴json转换工具Fastjson1.2.7. http://www.jb51.net/softs/530842.html 注意:jar包的导入不再赘述,详见百度. User类,定义两种属性,并创建构造方法与get和set方法 public class User { public String userName; //名字 public double balance; //金额 public User() { super(); } public User(Str

  • Java的JSON格式转换库GSON的初步使用笔记

    现在已经有一些能将Java对象转换成JSON的开源项目了.但是大多数项目都要求你在类文件中加入Java注解,而当你无法改动源代码的时候这是无法做到的.并且它们也不支持Java泛型.但是Gson却将这两点作为自己非常重要的设计目标. 特点: 使用toJson()和fromJson()方法,就可以非常容易的完成Java对象到JSON的相互转换. 能将预先存在的无法修改的对象与JSON互相转换. 支持Java泛型的使用. 允许对象的个性化表达形式(representation). 支持各种复杂(拥有深

  • Java解析xml文件和json转换的方法(DOM4j解析)

    Java解析xml的方法有四种: DOM解析 SAX解析 JDOM解析 DOM4J解析 本次主要介绍使用DOM4j进行解析: 这里我主要使用两种方式去解析: xml文件内容 <?xml version="1.0" encoding="utf-8"?> <books> <book id="1"> <name>金苹果</name> <info>锄禾日当午</info>

  • java实现二维数组转json的方法示例

    本文实例讲述了java实现二维数组转json的方法.分享给大家供大家参考,具体如下: package Tsets; public class erweiTojson { public static void main(String[] args) { String[][] blogList = { {"2008/07/07", "NetBeans New and Cool", "Tim Boudreau"}, {"2008/07/07&

  • 详解XML,Object,Json转换与Xstream的使用

    详解XML,Object,Json转换与Xstream的使用 1.Xstream的特点: 这里直接引用Xstream官方的叙述: 灵活易用:在更高的层次上提供了简单.灵活.易用的统一接口,用户无需了解项目的底层细节 无需映射:大多数对象都可以在无需映射的情况下进行序列化与反序列化的操作 高速稳定:设计时力求达到的最重要的指标是解析速度快.占用内存少,以使之能够适用于大的对象处理或是对信息吞吐量要求高的系统 清晰易懂:项目采用reflection机制得到无冗余信息的XML文件.所生成 的XML文件

  • java后台处理前端传的json串方法

    关于json,就不在多做介绍了,直接进入正题: 举例子: 1.现在前台做了一个Ajax请求,往数据库中添加数据: var json='{"goodName":"果粒橙","goodTitle":"这是一种好喝的","goodMessage":"这个很好喝,你还想喝 吗........","goodCost":"2","goodPrice&

  • Java实现后台发送及接收json数据的方法示例

    本文实例讲述了Java实现后台发送及接收json数据的方法.分享给大家供大家参考,具体如下: 本篇博客试用于编写java后台接口以及两个项目之间的接口对接功能: 具体的内容如下: 1.java后台给指定接口发送json数据 package com.utils; import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.net.Htt

  • java JSON解析库Alibaba Fastjson用法详解

    本文实例讲述了java JSON解析库Alibaba Fastjson用法.分享给大家供大家参考,具体如下: Json是一种轻量级的数据交换格式,应该在一个程序员的开发生涯中是常接触的.简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言. 易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率. 现在主流的对象与 JSON 互转的工具很多,这里介绍的是阿里巴巴的开源库 - Fastjson.Fastjson是一个Java库,可用于将Java对象转换为其JSON表示.它还可用

随机推荐