Converting JSON To Map With Java 8 Without Dependencies
Starting with JDK 8u60+ the built-in Nashorn engine is capable to convert Json content into java.util.Map
. No external dependencies are required for parsing:
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Map;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
public class JSONParsingTest {
private ScriptEngine engine;
@Before
public void initEngine() {
ScriptEngineManager sem = new ScriptEngineManager();
this.engine = sem.getEngineByName("javascript");
}
@Test
public void parseJson() throws IOException, ScriptException {
String json = new String(Files.readAllBytes(/*path*/);
String script = "Java.asJSONCompatible(" + json + ")";
Object result = this.engine.eval(script);
assertThat(result, instanceOf(Map.class));
Map contents = (Map) result;
contents.forEach((t, u) -> {
//key-value pairs
});
}
}
Also see official Nashorn extensions documentation.
See you at Java EE Workshops at Munich Airport, Terminal 2 or Virtual Dedicated Workshops / consulting. Is Munich's airport too far? Learn from home: effectivejavaee.com.
Do you have any benchmarks? How does it compare to other parsers?
Posted by piotrek on October 20, 2015 at 07:40 AM CEST #
I am wondering about the performance since that mean we load the engine to memory ?
Posted by ndwijaya on October 20, 2015 at 07:53 AM CEST #
The JSON object could be an array too.
Posted by Behrang on October 20, 2015 at 12:58 PM CEST #
How would you write that recursively?
Posted by Dan Howard on October 20, 2015 at 02:12 PM CEST #
It strikes me as weird that you can do it through Nashhorn but not with the normal Java API?
Posted by Michel schudel on October 20, 2015 at 05:55 PM CEST #
Simple JMH Benchmark:
MyBenchmark.jackson threat 20 3437373.038 ± 26239.248 ops/s
MyBenchmark.nashorn thrpt 20 471690.327 ± 12214.032 ops/s
Code:
package com.innoq;
import java.util.Map;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
@State(Scope.Benchmark)
public class MyBenchmark {
private ScriptEngine engine;
private ObjectMapper mapper;
private String json;
@Setup
public void setUp() throws Exception {
engine = new ScriptEngineManager().getEngineByName("javascript");
mapper = new ObjectMapper();
json = "{ \"foo\": \"bar\", \"bar\": 1 }";
}
@Benchmark
public Object nashorn() throws Exception {
Map<String, Object> map = (Map<String, Object>) engine.eval("Java.asJSONCompatible(" + json + ")");
return map.get("foo");
}
@Benchmark
public Object jackson() throws Exception {
Map<String, Object> map = mapper.readValue(json, Map.class);
return map.get("foo");
}
}
Posted by Michael Vitz on October 21, 2015 at 10:22 AM CEST #
Hey Adam I have been trying to run your code using 8u65 and I'm getting exception with below stacktrace, any idea what could be the cause?
Exception in thread "main" javax.script.ScriptException: TypeError: Cannot call undefined in <eval> at line number 1
at jdk.nashorn.api.scripting.NashornScriptEngine.throwAsScriptException(NashornScriptEngine.java:455)
Caused by: <eval>:1 TypeError: Cannot call undefined
at jdk.nashorn.internal.runtime.ECMAErrors.error(ECMAErrors.java:57)
Posted by Dhawan Shringi on October 26, 2015 at 07:10 AM CET #
Hi Adam, do you know a way back? With the Map to a cool new JSON. ;)
Posted by Eberhard Mayer on July 06, 2017 at 11:09 AM CEST #
This throws an exception if your JSON starts with a JSON Array.
Here is the solution to that problem.
// Insane hack to parse JSON without any external JSON library
private List <Map <String, String>> parseJsonArray (String jsonString) throws ScriptException
{
String scriptString = "Java.asJSONCompatible(" + jsonString + ")";
Object resultObj = scriptEngine.eval (scriptString);
List <Map <String, String>> jsonArray = (List <Map <String, String>>) resultObj;
return jsonArray;
}
Posted by skanga on March 03, 2019 at 12:53 AM CET #