Predicate 結果で Function 実行する

任意の検査をして Function<T, R> を実行する。
これは、stream か、Optional filter 実行後に map あるいは flatMap を実行と
ラムダ式を連結して記述すれば済むことではある。

例)日付文字列→LocalDate

String str;
// TODO 日付文字列をstr に格納
LocalDate dt = Optional.ofNullable(str)
   .filter(e->Pattern.compile("^\\d{4}/(0[1-9]|1[012])/(0[1-9]|[12][0-9]|3[01])$")
         .matcher(e).matches())
   .map(e->LocalDate.parse(e, DateTimeFormatter.ofPattern("yyyy/MM/dd")))
   .orElse(LocalDate.of(2021, 4, 1));

Predicate 結果、成功なら処理をする Function<T, R> としてまとめたらどうであろう。

import java.io.Serializable;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
/**
 * PredicateFunction
 */
@FunctionalInterface
public interface PredicateFunction<T, R> extends Serializable{
   R apply(T t);

   static <T, R> Function<T, R> of(Predicate<T> p, PredicateFunction<T, R> f){
      return t-> p.test(t) ? f.apply(t) : null;
   }
   static <T, R> Function<T, R> of(Predicate<T> p, PredicateFunction<T, R> f, R r){
      return t-> p.test(t) ? f.apply(t) : r;
   }
   static <T, R> Function<T, R> of(Predicate<T> p, PredicateFunction<T, R> f, Supplier<R> s){
      return t-> p.test(t) ? f.apply(t) : s.get();
   }
}

Optional の実行で、filter を書かずに当てはめてみる。

Function<String, LocalDate> predicFunction = PredicateFunction.of(
   t->Pattern.compile("^\\d{4}/(0[1-9]|1[012])/(0[1-9]|[12][0-9]|3[01])$")
      .matcher(t.toString()).matches()
   , e->LocalDate.parse(e.toString(), DateTimeFormatter.ofPattern("yyyy/MM/dd"))
);
LocalDate date = Optional.ofNullable(str).map(predicFunction)
.orElse(LocalDate.of(2021, 4, 1));

今回の例は、Pattern の matcher メソッドが、CharSequenceを引数であるため、
Optional による null チェックが入り、Object の toString() を実行しているのでこのようになっている、

だから、Optional でなく、PredicateFunction の apply メソッド実行で書けば、、、

LocalDate date = PredicateFunction.of(
   t->Pattern.compile("^\\d{4}/(0[1-9]|1[012])/(0[1-9]|[12][0-9]|3[01])$")
      .matcher(t==null ? "" :t.toString()).matches()
   , e->LocalDate.parse(e.toString(), DateTimeFormatter.ofPattern("yyyy/MM/dd"))
   , LocalDate.of(2021, 4, 1)
).apply(str);

PredicateFunction.of による Function<T, R> を予め生成しておく方法としては
少しばかり有効な手段かもしれない。

クラスと同じclasspath の場所に置いたファイルのIO

resources ではなく、Javaソースと同じ場所にファイルを置いて
ビルド後の classes の同じパッケージ階層に配置されたファイルを読み込む方法、

java 拡張子以外のファイルもビルドで、class と共に同じ場所に配置されるころが前提条件であるが、

以下のように、カレントスレッドのスタックトレースから場所を割り出せば
そのファイルも読める。

public File getCurrentPathFile(String path) throws IOException{
   try{
      return Optional.ofNullable(
         ClassLoader.getSystemClassLoader().getResource(
            Class.forName(Thread.currentThread().getStackTrace()[2].getClassName())
            .getPackage().getName().replaceAll("\\.", "/") + "/" + path
         )
      ).map(u->{
            try{
               return new File(u.toURI());
            }catch(URISyntaxException e){
               throw new RuntimeException(e);
            }
      }).orElseThrow(()->new IOException(path + " is not Found"));
   }catch(ClassNotFoundException e){
      throw new RuntimeException(e);
   }
}

↑より、ファイルを同じPATH に配置したクラスを指定する方が良いと思う。

public File getPathFile(Class<?> cls, String path) throws IOException{
   try{
      return new File(ClassLoader.getSystemClassLoader()
         .getResource(cls.getPackage().getName().replaceAll("\\.", "/") + "/" + path).toURI());
   }catch(URISyntaxException e){
      throw new RuntimeException(e);
   }
}

通常、Java アーキテクチャのスタイルとして、Java クラスと同じ場所に
リソース扱いのファイルなど置かないのであろうが、
クラスと同じ場所に置いた方が管理しやすい場合もあるだろう。

gson でJSON読み込んで、Key と Value

先日、Jackson でJSON読込み key-value の BiConsumer を処理する - Oboe吹きプログラマの黙示録

Jackson でJSON読込み key-value の Stream生成 - Oboe吹きプログラマの黙示録
を書いたので、gson で同じことをします。

JSONキーは、"." で区切り、配列は、[n] でインデックスを表現します。

import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringReader;
import java.nio.charset.StandardCharsets;
import java.util.AbstractMap.SimpleEntry;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.function.BiConsumer;
import java.util.function.Predicate;
import java.util.stream.Stream;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.JsonPrimitive;
/**
 * JsonView
 */
public class JsonView{

   public void read(String jsontxt, BiConsumer<String, Object> biconsumer){
      parseElement(JsonParser.parseReader(new StringReader(jsontxt)), "", biconsumer);
   }
   public void read(Reader reader, BiConsumer<String, Object> biconsumer){
      parseElement(JsonParser.parseReader(reader), "", biconsumer);
   }
   public void read(InputStream in, BiConsumer<String, Object> biconsumer){
      parseElement(JsonParser.parseReader(new InputStreamReader(in, StandardCharsets.UTF_8)), "", biconsumer);
   }
   private void parseElement(JsonElement je, String parent, BiConsumer<String, Object> biconsumer){
      if (je.isJsonObject()) {
         JsonObject jo = (JsonObject)je;
         for(Entry<String, JsonElement> e:jo.entrySet()){
            parseElement(e.getValue(), parent + "." + e.getKey(), biconsumer);
         }
      }else if(je.isJsonArray()){
         JsonArray ary = je.getAsJsonArray();
         int i = 0;
         for(Iterator<JsonElement> it=ary.iterator();it.hasNext();i++){
            parseElement(it.next(), parent + "[" + i + "]", biconsumer);
         }
      }else if(je.isJsonNull()){
         biconsumer.accept(parent.substring(1), null);
      }else if(je.isJsonPrimitive()){
         String path = parent.substring(1);
         JsonPrimitive p = je.getAsJsonPrimitive();
         if (p.isNumber()){
            if (je.toString().indexOf(".") > 0) {
               biconsumer.accept(path, p.getAsDouble());
            }else{
               if (p.getAsLong() <= Integer.MAX_VALUE) {
                  biconsumer.accept(path, p.getAsLong());
               }else{
                  biconsumer.accept(path, p.getAsInt());
               }
            }
         }else if(p.isBoolean()){
            biconsumer.accept(path, p.getAsBoolean());
         }else if(p.isString()){
            biconsumer.accept(path, p.getAsString());
         }
      }
   }

   public void read(String jsontxt, Predicate<String> predicate, BiConsumer<String, Object> biconsumer){
      parseElement(JsonParser.parseReader(new StringReader(jsontxt)), "", predicate, biconsumer);
   }
   public void read(Reader reader, Predicate<String> predicate, BiConsumer<String, Object> biconsumer){
      parseElement(JsonParser.parseReader(reader), "", predicate, biconsumer);
   }
   public void read(InputStream in, Predicate<String> predicate, BiConsumer<String, Object> biconsumer){
      parseElement(JsonParser.parseReader(new InputStreamReader(in, StandardCharsets.UTF_8)), "", predicate, biconsumer);
   }
   private void parseElement(JsonElement je, String parent, Predicate<String> predicate, BiConsumer<String, Object> biconsumer){
      if (je.isJsonObject()) {
         JsonObject jo = (JsonObject)je;
         for(Entry<String, JsonElement> e:jo.entrySet()){
            parseElement(e.getValue(), parent + "." + e.getKey(), biconsumer);
         }
      }else if(je.isJsonArray()){
         JsonArray ary = je.getAsJsonArray();
         int i = 0;
         for(Iterator<JsonElement> it=ary.iterator();it.hasNext();i++){
            parseElement(it.next(), parent + "[" + i + "]", biconsumer);
         }
      }else if(je.isJsonNull()){
         String path = parent.substring(1);
         if (predicate.test(path)){
            biconsumer.accept(path, null);
         }
      }else if(je.isJsonPrimitive()){
         String path = parent.substring(1);
         if (predicate.test(path)){
            JsonPrimitive p = je.getAsJsonPrimitive();
            if (p.isNumber()){
               if (je.toString().indexOf(".") > 0) {
                  biconsumer.accept(path, p.getAsDouble());
               }else{
                  if (p.getAsLong() <= Integer.MAX_VALUE) {
                     biconsumer.accept(path, p.getAsLong());
                  }else{
                     biconsumer.accept(path, p.getAsInt());
                  }
               }
            }else if(p.isBoolean()){
               biconsumer.accept(path, p.getAsBoolean());
            }else if(p.isString()){
               biconsumer.accept(path, p.getAsString());
            }
         }
      }
   }
   public Stream<Entry<String, Object>> stream(InputStream in){
      Stream.Builder<Entry<String, Object>> builder = Stream.builder();
      parseElement(JsonParser.parseReader(new InputStreamReader(in, StandardCharsets.UTF_8)), "", builder);
      return  builder.build();
   }
   public Stream<Entry<String, Object>> stream(String jsontxt){
      Stream.Builder<Entry<String, Object>> builder = Stream.builder();
      parseElement(JsonParser.parseReader(new StringReader(jsontxt)), "", builder);
      return  builder.build();
   }
   public Stream<Entry<String, Object>> stream(Reader reader){
      Stream.Builder<Entry<String, Object>> builder = Stream.builder();
      parseElement(JsonParser.parseReader(reader), "", builder);
      return  builder.build();
   }

   private void parseElement(JsonElement je, String parent, Stream.Builder<Entry<String, Object>> builder){
      if (je.isJsonObject()) {
         JsonObject jo = (JsonObject)je;
         for(Entry<String, JsonElement> e:jo.entrySet()){
            parseElement(e.getValue(), parent + "." + e.getKey(), builder);
         }
      }else if(je.isJsonArray()){
         JsonArray ary = je.getAsJsonArray();
         int i = 0;
         for(Iterator<JsonElement> it=ary.iterator();it.hasNext();i++){
            parseElement(it.next(), parent + "[" + i + "]", builder);
         }
      }else if(je.isJsonNull()){
         builder.add(new SimpleEntry<String, Object>(parent.substring(1), null));
      }else if(je.isJsonPrimitive()){
         String path = parent.substring(1);
         JsonPrimitive p = je.getAsJsonPrimitive();
         if (p.isNumber()){
            if (je.toString().indexOf(".") > 0) {
               builder.add(new SimpleEntry<String, Object>(path, p.getAsDouble()));
            }else{
               if (p.getAsLong() <= Integer.MAX_VALUE) {
                  builder.add(new SimpleEntry<String, Object>(path, p.getAsLong()));
               }else{
                  builder.add(new SimpleEntry<String, Object>(path, p.getAsInt()));
               }
            }
         }else if(p.isBoolean()){
            builder.add(new SimpleEntry<String, Object>(path, p.getAsBoolean()));
         }else if(p.isString()){
            builder.add(new SimpleEntry<String, Object>(path, p.getAsString()));
         }
      }
   }
}

今頃になってこんなものと思いつつ、、
jsonview · yipuran/yipuran-gsonhelper Wiki · GitHub
として Git-Hub に入れた。もっと早く作ってればよかった。

Jackson でJSON読込み key-value の Stream生成

先日の
oboe2uran.hatenablog.com

さらに、以下のメソッドも追加するとべ便利かもしれない。

public Stream<Entry<String, Object>> stream(String jsontxt){
   Stream.Builder<Entry<String, Object>> builder = Stream.builder();
   ObjectMapper mapper = new ObjectMapper();
   try{
      parseJson(mapper.readTree(jsontxt), "", builder);
   }catch (JsonProcessingException e){
      throw new RuntimeException(e);
   }
   return  builder.build();
}

public Stream<Entry<String, Object>> stream(InputStream in){
   Stream.Builder<Entry<String, Object>> builder = Stream.builder();
   ObjectMapper mapper = new ObjectMapper();
   try{
      parseJson(mapper.readTree(in), "", builder);
   }catch (JsonProcessingException e){
      throw new RuntimeException(e);
   }catch(IOException e){
      throw new RuntimeException(e);
   }
   return  builder.build();
}

private void parseJson(JsonNode node, String path, Stream.Builder<Entry<String, Object>> builder){
   if (node.getNodeType().equals(JsonNodeType.OBJECT)) {
      for(Iterator<Entry<String, JsonNode>> it=node.fields(); it.hasNext();) {
         Entry<String, JsonNode> entry = it.next();
         parseJson(entry.getValue(), path + "." + entry.getKey(), builder);
      }
   }else if(node.getNodeType().equals(JsonNodeType.ARRAY)){
      int x=0;
      for(Iterator<JsonNode> it=node.iterator(); it.hasNext();x++){
         parseJson(it.next(), path + "[" + x + "]", builder);
      }
   }else if(node.getNodeType().equals(JsonNodeType.NULL)){
      builder.add(new SimpleEntry<String, Object>(path.substring(1), null));
   }else if(node.getNodeType().equals(JsonNodeType.NUMBER)){
      if (node.isDouble()){
         builder.add(new SimpleEntry<String, Object>(path.substring(1), node.asDouble()));
      }else if(node.isLong()){
         builder.add(new SimpleEntry<String, Object>(path.substring(1), node.asLong()));
      }else{
         builder.add(new SimpleEntry<String, Object>(path.substring(1), node.asInt()));
      }
   }else if(node.getNodeType().equals(JsonNodeType.BOOLEAN)){
      builder.add(new SimpleEntry<String, Object>(path.substring(1), node.asBoolean()));
   }else if(node.getNodeType().equals(JsonNodeType.STRING)){
      builder.add(new SimpleEntry<String, Object>(path.substring(1), node.asText()));
   }
}

Jackson でJSON読込み key-value の BiConsumer を処理する

JSON を Jackson の ObjectMapper でクラスにマッピングするのではなく、
JSON キー と 値の BiConsumer を実行する。

ObjectMapper readTree メソッドで取得する JsonNode を使用する。

import java.io.IOException;
import java.io.InputStream;
import java.util.AbstractMap.SimpleEntry;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.function.BiConsumer;
import java.util.function.Predicate;
import java.util.stream.Stream;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.JsonNodeType;
/**
 * JsonNodeParse
 */
public class JsonNodeParse{

   public void readJson(String jsontxt, BiConsumer<String, Object> con){
      ObjectMapper mapper = new ObjectMapper();
      try{
         parseJson(mapper.readTree(jsontxt), "", con);
      }catch (JsonProcessingException e){
         throw new RuntimeException(e);
      }
   }

   public void readJson(InputStream in, BiConsumer<String, Object> con){
      ObjectMapper mapper = new ObjectMapper();
      try{
         parseJson(mapper.readTree(in), "", con);
      }catch (JsonProcessingException e){
         throw new RuntimeException(e);
      }catch(IOException e){
         throw new RuntimeException(e);
      }
   }

   private void parseJson(JsonNode node, String path, BiConsumer<String, Object> con){
      if (node.getNodeType().equals(JsonNodeType.OBJECT)) {
         for(Iterator<Entry<String, JsonNode>> it=node.fields(); it.hasNext();) {
            Entry<String, JsonNode> entry = it.next();
            parseJson(entry.getValue(), path + "." + entry.getKey(), con);
         }
      }else if(node.getNodeType().equals(JsonNodeType.ARRAY)){
         int x=0;
         for(Iterator<JsonNode> it=node.iterator(); it.hasNext();x++){
            parseJson(it.next(), path + "[" + x + "]", con);
         }
      }else if(node.getNodeType().equals(JsonNodeType.NULL)){
         con.accept(path.substring(1), null);
      }else if(node.getNodeType().equals(JsonNodeType.NUMBER)){
         if (node.isDouble()){
            con.accept(path.substring(1), node.asDouble());
         }else if(node.isLong()){
            con.accept(path.substring(1), node.asLong());
         }else{
            con.accept(path.substring(1), node.asInt());
         }
      }else if(node.getNodeType().equals(JsonNodeType.BOOLEAN)){
         con.accept(path.substring(1), node.asBoolean());
      }else if(node.getNodeType().equals(JsonNodeType.STRING)){
         con.accept(path.substring(1), node.asText());
      }
   }


   public void readJson(String jsontxt, Predicate<String> pathfilter, BiConsumer<String, Object> con){
      ObjectMapper mapper = new ObjectMapper();
      try{
         parseJson(mapper.readTree(jsontxt), "", pathfilter, con);
      }catch (JsonProcessingException e){
         throw new RuntimeException(e);
      }
   }

   public void readJson(InputStream in, Predicate<String> pathfilter, BiConsumer<String, Object> con){
      ObjectMapper mapper = new ObjectMapper();
      try{
         parseJson(mapper.readTree(in), "", pathfilter, con);
      }catch (JsonProcessingException e){
         throw new RuntimeException(e);
      }catch(IOException e){
         throw new RuntimeException(e);
      }
   }
   private void parseJson(JsonNode node, String path, Predicate<String> pathfilter, BiConsumer<String, Object> con){
      if (node.getNodeType().equals(JsonNodeType.OBJECT)) {
         for(Iterator<Entry<String, JsonNode>> it=node.fields(); it.hasNext();) {
            Entry<String, JsonNode> entry = it.next();
            parseJson(entry.getValue(), path + "." + entry.getKey(), pathfilter, con);
         }
      }else if(node.getNodeType().equals(JsonNodeType.ARRAY)){
         int x=0;
         for(Iterator<JsonNode> it=node.iterator(); it.hasNext();x++){
            parseJson(it.next(), path + "[" + x + "]", pathfilter, con);
         }
      }else if(node.getNodeType().equals(JsonNodeType.NULL)){
         String p = path.substring(1);
         if (pathfilter.test(p)){
            con.accept(p, null);
         }
      }else if(node.getNodeType().equals(JsonNodeType.NUMBER)){
         String p = path.substring(1);
         if (pathfilter.test(p)){
            if (node.isDouble()){
               con.accept(p, node.asDouble());
            }else if(node.isLong()){
               con.accept(p, node.asLong());
            }else{
               con.accept(p, node.asInt());
            }
         }
      }else if(node.getNodeType().equals(JsonNodeType.BOOLEAN)){
         String p = path.substring(1);
         if (pathfilter.test(p)){
            con.accept(p, node.asBoolean());
         }
      }else if(node.getNodeType().equals(JsonNodeType.STRING)){
         String p = path.substring(1);
         if (pathfilter.test(p)){
            con.accept(p, node.asText());
         }
      }
   }
}

JSON キーは、"." 区切りで連結し、配列は、"[n]" で表現する

使用例

JsonNodeParse jsonNodeparse = new JsonNodeParse();
jsonNodeparse.readJson(jsontxt, (p, o)->{
   System.out.println("path = " + p + "  value = "+ o);
});

JSON

{
   "id" : 1207,
   "name" : "テストFoo",
   "cat" : [ 22, 33 ],
   "mod" : null,
   "flg" : true,
   "group" : {
      "hub": "Test",
      "length": 120.45
   }
}

結果

path = id  value = 1207
path = name  value = テストFoo
path = cat[0]  value = 22
path = cat[1]  value = 33
path = mod  value = null
path = flg  value = true
path = group.hub  value = Test
path = group.length  value = 120.45

また、おまけのようではあるが、
Json キー、パスの Predicate で

JsonNodeParse jsonNodeparse = new JsonNodeParse();
jsonNodeparse.readJson(jsontxt, p->p.startsWith("group.")
, (p, o)->{
   System.out.println("path = " + p + "  value = "+ o);
});
path = group.hub  value = Test
path = group.length  value = 120.45

gson や Jackson に頼らない JSON の読込み

非奨励の nashorn 使用する方法でなく、別の方法です。
有名な Graal VM の助けを借りる方法です。
https://www.graalvm.org/
準備:
Maven  pom.xml

<dependency>
   <groupId>org.graalvm.js</groupId>
   <artifactId>js</artifactId>
   <version>21.0.0.2</version>
</dependency>
<dependency>
   <groupId>org.graalvm.js</groupId>
   <artifactId>js-scriptengine</artifactId>
   <version>21.0.0.2</version>
</dependency>

前回同様、JSON のキーのパスを指定して値を取得するものです。

import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Queue;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Value;
/**
 * JsonBody
 */
public class JsonBody{
   private Value root;

   public JsonBody(String json) {
      Context context = Context.newBuilder("js").build();
      root = context.eval("js", "var obj=" + json+ "; obj;");
   }

   public static JsonBody of(String json){
      return new JsonBody(json);
   }

   public Object getObject(String path) {
      Queue<String> queue = Arrays.stream(path.split("\\."))
         .collect(ArrayDeque<String>::new, (r, t)->r.add(t), (r, t)->{});
      Value value = root;
      Object o = root;
      do{
         if (o==null){
            throw new IllegalArgumentException(path + " is Refreence null");
         }
         if (o instanceof Map) {
            o = ((Map<String, Object>)o).get(queue.poll());
         }else{
            value = (Value)o;
            o = getJavaObject(value.getMember(queue.poll()));
         }
      }while(!queue.isEmpty());
      return o;
   }

   @SuppressWarnings("unchecked")
   public <R> R get(String path) {
      return (R)getObject(path);
   }

   private Object getJavaObject(Value value) {
      if (value.isNull()) return null;
      if (value.isBoolean()) return value.asBoolean();
      if (value.isDate()) return value.asDate();
      if (value.isTime()) return value.asTime();
      if (value.fitsInInt()) return value.asInt();
      if (value.fitsInLong()) return value.asLong();
      if (value.fitsInDouble()) return value.asDouble();
      if (value.isString()) return value.asString();
      if (value.hasMembers()){
         if (value.hasArrayElements()){
            Object[] o = new Object[(int)value.getArraySize()];
            for(int i=0;i < value.getArraySize();i++) {
               o[i] = getJavaObject(value.getArrayElement(i));
            }
            return o;
         }else{
            Map<String, Object> map = new HashMap<String, Object>();
            value.getMemberKeys().stream()
               .forEach(e->map.put(e, getJavaObject(value.getMember(e))));
            return map;
         }
      }
      return value;
   }
}

JSON サンプル

{
   "name" : "テスト",
   "width" : 228,
   "length" : 12.53,
   "points" : [ 14, 8, 21 ],
   "flg" : true,
   "unit" : {
      "color": "Red",
      "rank": 12
   }
}

呼び出しサンプル

JsonBody body = JsonBody.of(jsontxt);

String name = body.get("name");
String color = body.get("unit.color");
Object[] points = body.get("points");
Map<String, Object> unit = body.get("unit");

上の JsonBody の getJavaObject メソッドの中の

     if (value.isDate()) return value.asDate();

org.graalvm.polyglot.Value で Date型か問い合わせて asDate() で、java.time.LocalDate に変換する
ことを作用させるJSON は、結局、JavaScript の Date型を値にしたJSON 書式で

{
     "date" :  new Date()
}

でないと動かない。
JavaScript としての JSON の書式で new Date() であり、同じ解析は、Jackson や、
Google gson ではできないでしょう。new Date()記述を一度文字列として読込んで
LocalDate.now() を実行しなくてはなりません。純粋なJSON解析とは言えなくなります。
また、java.time.LocalTime を望む isTime() や、 asTime() を働かせる方法が良くわからない。

{
      "time": (new Date()).toJSON()
}

これでは、asString() で処理されてしまう。

gson や Jackson に頼らない JSON の読込み(非奨励の方法)

標準ライブラリにある javax.script.ScriptEngine で、
JavaScript として解釈させて Nashorn で参照する方法です。
Google gson みたいな高機能は期待できません。
Nashorn は、Java11 から非奨励です。
Java15 から廃止されてます

書いては、みたものの、非奨励だから使わない方法です。

import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import jdk.nashorn.api.scripting.ScriptObjectMirror;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
/**
 * JsonBody
 */
public class JsonBody{
   private ScriptEngine engine;

   public JsonBody(String json) throws ScriptException{
      ScriptEngineManager manager = new ScriptEngineManager();
      engine = manager.getEngineByName("JavaScript");
      String script = "var _obj=" + json + ";";
      engine.eval(script);
   }
   
   public static JsonBody of(String json) throws ScriptException{
      return new JsonBody(json);
   }

   public Object getObject(String path) {
      try{
         return engine.eval("_obj." + path);
      }catch(ScriptException e){
         e.printStackTrace();
      }
      return null;
   }
   public String getString(String path) {
      try{
         return Optional.ofNullable(engine.eval("_obj." + path))
               .map(e->e.toString()).orElse(null);
      }catch(ScriptException e){
         e.printStackTrace();
      }
      return null;
   }
   public Integer getInt(String path) {
      try{
         return Optional.ofNullable(engine.eval("_obj." + path))
               .map(e->Integer.valueOf(e.toString())).orElse(null);
      }catch(ScriptException e){
         e.printStackTrace();
      }
      return null;
   }
   public Double getDouble(String path) {
      try{
         return Optional.ofNullable(engine.eval("_obj." + path))
               .map(e->Double.valueOf(e.toString())).orElse(null);
      }catch(ScriptException e){
         e.printStackTrace();
      }
      return null;
   }

   public Object[] getObjectArray(String path) {
      try{
          ScriptObjectMirror mirror = (ScriptObjectMirror)engine.eval("_obj." + path);
          if (mirror.isArray()){
             return mirror.to(Object[].class);
          }else{
             return new Object[]{};
          }
      }catch(ScriptException e){
         e.printStackTrace();
      }
      return new Object[]{};
   }
   public Integer[] getIntArray(String path) {
      try{
          ScriptObjectMirror mirror = (ScriptObjectMirror)engine.eval("_obj." + path);
          if (mirror.isArray()){
             return mirror.to(Integer[].class);
          }else{
             return new Integer[]{};
          }
      }catch(ScriptException e){
         e.printStackTrace();
      }
      return new Integer[]{};
   }
   public Double[] getDoubleArray(String path) {
      try{
          ScriptObjectMirror mirror = (ScriptObjectMirror)engine.eval("_obj." + path);
          if (mirror.isArray()){
             return mirror.to(Double[].class);
          }else{
             return new Double[]{};
          }
      }catch(ScriptException e){
         e.printStackTrace();
      }
      return new Double[]{};
   }
   @SuppressWarnings("unchecked")
   public Map<String, Object> getMap(String path) {
      try{
          return (Map<String, Object>)convertIntoJavaObject(engine.eval("_obj." + path));
      }catch(ScriptException e){
         e.printStackTrace();
      }
      return new HashMap<>();
   }
   private Object convertIntoJavaObject(Object scriptObj) {
      if (scriptObj instanceof ScriptObjectMirror) {
         ScriptObjectMirror scriptObjectMirror = (ScriptObjectMirror) scriptObj;
         if (scriptObjectMirror.isArray()) {
            List<Object> list = Lists.newArrayList();
            for (Map.Entry<String, Object> entry : scriptObjectMirror.entrySet()){
               list.add(convertIntoJavaObject(entry.getValue()));
            }
            return list;
         }else   {
            Map<String, Object> map = Maps.newHashMap();
            for(Map.Entry<String, Object> entry : scriptObjectMirror.entrySet()){
               map.put(entry.getKey(), convertIntoJavaObject(entry.getValue()));
            }
            return map;
         }
      }else{
         return scriptObj;
      }
   }
}

読込みパスの指定:
 JavaScript の世界の Path の指定と同じ、"." ドット(ピリオド)で区切って指定します。
例)

{
     "a" : "A00",
     "b" : 11,
     "c" : {
          "d" : [ 1, 2, 3 ],
          "e" :  "E"
     }
}

呼び出し例

JsonBody body = new JsonBody(json);
String e = body.getString("c.e");
Integer[] ary = body.getIntArray("c.d");

(注意)
配列の読込や Map としての読込の為に、
 jdk.nashorn.api.scripting.ScriptObjectMirror
を使ってます。
これは、通常のビルドでは、
Eclipse デフォルト設定のままでは、アクセス制限の警告が出ます。。
The type 'ScriptObjectMirror' is not API
(restriction on required library 'C:\Java\jdk1.8.0_231\jre1.8.0_231\lib\ext\nashorn.jar')

警告なので、動くことは動きますが、ビルドパス指定した方が良さそうです。