用 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 的都支持。所以完全可以这么来做。

我本人很早就这么做了,见之前的博文《使用 Rhino 作为 Java 的 JSON 解析/转换包》。只是早期 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 的异常的。

不知道大家还有什么建议和意见,欢迎提出!

时间: 2024-09-15 13:45:40

用 Rhino/Nashorn 代替第三方 JSON 转换库的相关文章

用 Rhino/Nashorn 代替第三方 JSON 转换库(第三版)

无须其他第三方包,只是依赖于 Java 自带的 JVM 自带的 Rhino/Nashorn 引擎提供 js/json 的服务.主要的两个类是 JsEngineWrapper.JsonHelper,它们的继承关系是 JsEngineWrapper 派生了 JsonHelper. 源码:http://git.oschina.net/sp42/ajaxjs/tree/master/ajaxjs-base/src/com/ajaxjs/js?dir=1&filepath=ajaxjs-base%2Fsr

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

下载和部署GSONGSON的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. <

使用 Rhino 作为 Java 的 JSON 解析/转换包

前端开发者是幸福的,源自于浏览器对 JSON 天然的支持(JSON 本身脱胎于 JavaScript),JSON 字符串一下子 eval() 或者 JSON.parse() 就可以直接使用了:输出 JSON 字符反之亦然. 如果是 JS 的老大哥 Java 呢?这个问题大家应该都会不约而同地回答:一般从接口转换 Java 对象为 JSON 输出的时候都会选择相关的 JSON-lib,有的是 JSON.org 的,有的是 Jackson JSON,有的是 FastJSON 的,有的是 GSON的,

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

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

【原创】各种 JSON 解析库的功能简介

这里增加一项无聊的对比图,看客自斟. =================================== [rui_maciel/mjson]Last Update:2013-05-15(最新版本为 mjson-1.5 发布日期为 2012-08-22)description M's JSON parser is a small JSON parser written in ISO C which enables the user to handle information describe

Json操作库DynamicJson使用指南_C#教程

Json的简介 JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式.它基于ECMAScript的一个子集. JSON采用完全独立于语言的文本格式,但是也使用了类似于C语言家族的习惯(包括C.C++.C#.Java.JavaScript.Perl.Python等).这些特性使JSON成为理想的数据交换语言. 易于人阅读和编写,同时也易于机器解析和生成(一般用于提升网络传输速率). Json的优点 数据格式比较简单,易于读写,格式都是压缩的,占用带宽小 易于

Android JSON解析库Gson和Fast-json的使用对比和图书列表小案例

Android JSON解析库Gson和Fast-json的使用对比和图书列表小案例 继上篇json解析,我用了原生的json解析,但是在有些情况下我们不得不承认,一些优秀的json解析框架确实十分的好用,今天我们为了博客的保质保量,也就不分开写,我们直接拿比较火的Gson和Fast-json来使用,末尾在进行一些分析 Android JSON原生解析的几种思路,以号码归属地,笑话大全,天气预报为例演示 一.各有千秋 两大解析库的东家都是巨头,一个来自于Google官方,一个来自阿里巴巴,我们这

C#将XML转换成JSON转换XML

原文:C#将XML转换成JSON转换XML using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Xml; using Newtonsoft.Json; namespace JSonConverter { class Program { static void Main(string[] args) { string xml = "<Test>

将json转换成struts参数的方法_javascript技巧

加入对象为{name:'tom','class':{className:'class1'},classMates:[{name:'lily'}]} struts2期待的格式是 name=tom&class.className=class1&classMates[0].name=lily function parseParam(param, key) { var paramStr = ""; if (param instanceof String || param ins