JsonProvide.java
public interface JsonProvider { public Object fromJson(String json); public String toJson(Object object); }JsonProvider is an interface which is used later to access correspondent JavaScript methods.
jsonTest.js
var jsonProvider = new Object(); // produces an JavaScript object or array from an JSON text. jsonProvider.fromJson = function(json) { var obj = JSON.parse(json); return makeTestable(obj); }; // produces an JSON text from an JavaScript object or array jsonProvider.toJson = function(object) { var obj = makeTestable(object); return JSON.stringify(obj); }; function makeTestable(obj) { obj.getValue = function(property) { return this[property]; }; return obj; } // Test object var circle = { uuid: "567e6162-3b6f-4ae2-a171-2470b63dff00", x: 10, y: 20, movedToFront: true, rotationDegree: 90, radius: 50, backgroundColor: "#FF0000", borderColor: "#DDDDDD", borderWidth: 1, borderStyle: "-", backgroundOpacity: 1.0, borderOpacity: 0.5, scaleFactor: 1.2 };There are two methods fromJson / toJson and a helper function makeTestable in order to get any value of JavaScript objects from Java. The test object in JavaScript is called circle. The corresponding Java class is called Circle and has the same fields with getter / setter. We can write an JUnit test now.
import com.google.gson.Gson; import com.googlecode.whiteboard.model.Circle; import org.apache.commons.beanutils.PropertyUtils; import org.junit.AfterClass; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; import javax.script.Invocable; import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; import javax.script.ScriptException; import java.io.FileNotFoundException; import java.util.Map; import java.util.Set; import java.util.UUID; public class JsonTest { private static Gson gson; private static ScriptEngine engine; private static JsonProvider jsonProvider; @BeforeClass public static void runBeforeClass() { // create Gson GsonBuilder gsonBilder = new GsonBuilder(); gson = gsonBilder.serializeNulls().create(); // create a script engine manager ScriptEngineManager factory = new ScriptEngineManager(); // create JavaScript engine engine = factory.getEngineByName("JavaScript"); try { // evaluate JavaScript code from the json2 library and the test file engine.eval(new java.io.FileReader("src/main/webapp/resources/js/json2-min.js")); engine.eval(new java.io.FileReader("src/test/resources/js/jsonTest.js")); // get an implementation instance of the interface JsonProvider from the JavaScript engine, // all interface's methods are implemented by script methods of JavaScript object jsonProvider Invocable inv = (Invocable) engine; jsonProvider = inv.getInterface(engine.get("jsonProvider"), JsonProvider.class); } catch (ScriptException e) { e.printStackTrace(); } catch (FileNotFoundException e) { e.printStackTrace(); } } @AfterClass public static void runAfterClass() { gson = null; jsonProvider = null; } @Test public void JavaScript2Java() { // get JavaScript object Object circle1 = engine.get("circle"); // client-side: make JSON text from JavaScript object String json = jsonProvider.toJson(circle1); // server-side: convert JSON text to Java object Circle circle2 = gson.fromJson(json, Circle.class); // compare two objects testEquivalence(circle2, circle1); } @Test public void Java2JavaScript() { // create Java object Circle circle1 = new Circle(); circle1.setUuid(UUID.randomUUID().toString()); circle1.setX(100); circle1.setY(100); circle1.setMovedToFront(false); circle1.setRotationDegree(0); circle1.setRadius(250); circle1.setBackgroundColor("#FFFFFF"); circle1.setBorderColor("#000000"); circle1.setBorderWidth(3); circle1.setBorderStyle("."); circle1.setBackgroundOpacity(0.2); circle1.setBorderOpacity(0.8); circle1.setScaleFactor(1.0); // server-side: convert Java object to JSON text String json = gson.toJson(circle1); // client-side: make JavaScript object from JSON text Object circle2 = jsonProvider.fromJson(json); // compare two objects testEquivalence(circle1, circle2); } @SuppressWarnings("unchecked") private void testEquivalence(Object obj1, Object obj2) { try { Map<String, Object> map = PropertyUtils.describe(obj1); Set<String> fields = map.keySet(); Invocable inv = (Invocable) engine; for (String key : fields) { Object value1 = map.get(key); if (!key.equals("class")) { Object value2 = inv.invokeMethod(obj2, "getValue", key); if (value1 instanceof Number && !(value1 instanceof Double)) { // JS number is always converted to Java double ==> only doubles can be compared, // see http://www.mozilla.org/js/liveconnect/lc3_method_overloading.html value1 = new Double(value1.toString()); } Assert.assertEquals("Value of property '" + key + "' was wrong converted", value2, value1); } } } catch (Exception e) { throw new IllegalStateException("Equivalence test of two objects failed!", e); } } }I create a Gson and a ScriptEngine instances in the method runBeforeClass() and load all needed scripts into the ScriptEngine. After that I get an implementation instance of the interface JsonProvider from the JavaScript engine. Now I'm able to call JavaScript methods from my JsonProvider implementation. There are two tests:
@Test public void JavaScript2Java()
I test here the use case if an JavaScript object (circle) gets converted to an JSON text, sent to the server and converted there to an Java object (Circle). The original JavaScript object and the result Java object are compared afterwards.
@Test public void Java2JavaScript()
I test here the use case if a created Java object (Circle) gets converted to an JSON text, sent to the client and converted there to an JavaScript object (circle). The objects are compared to ensure their equivalence.
You can also use "JavaScript to Java Communication" with Java Scripting API and access Java classes, objects and methods from JavaScript. Pretty cool.