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.

Comments:

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 #

Post a Comment:
  • HTML Syntax: NOT allowed
...the last 150 posts
...the last 10 comments
License