4

私は、JavaJSR-223APIを介してRhinoで評価しているJavaScriptを使用してJSONファイルを読み取ろうとしています。私がやろうとしていることは、Rhinoシェルでは機能しますが、Javaコードに埋め込まれたJavascriptでは機能しません。

動作するものは次のとおりです。

$ java -jar js.jar
js> readFile('foo.json');
{y:"abc"}

動作しないものは次のとおりです。

ScriptEngineManager factory = new ScriptEngineManager();
ScriptEngine engine = factory.getEngineByName("js");
engine.eval("readFile('foo.json')"); 

このエラーが発生します:

Exception in thread "main" javax.script.ScriptException: 
sun.org.mozilla.javascript.internal.EcmaError: ReferenceError: "readFile" is not defined. (<Unknown source>#4) in <Unknown source> at line number 4
    at com.sun.script.javascript.RhinoScriptEngine.eval(RhinoScriptEngine.java:153)
    at javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:232)

readFileエンジンでを利用できるようにするにはどうすればよいですか?

4

2 に答える 2

5

JSONを解析しようとしているだけの場合は、 JacksonJettisonなどの JSON 解析ライブラリを使用することをお勧めします。これははるかに簡単な方法です。readFile 呼び出しは文字列を返しますが、実際にはコンテンツを JavaScript オブジェクトに変換していません。そのためには、文字列に対するJSON.parse またはJavaScript eval呼び出しが必要になります。

JavaScript エンジンを使用して JSON を解析する必要があり、Java 7 を使用している場合は、次のようなことができます (ただし、お勧めしません)...

public static void main(final String args[]) throws Exception {
    final ScriptEngineManager factory = new ScriptEngineManager();
    final ScriptEngine engine = factory.getEngineByName("js");
    Object val = null;
    Scanner s = null;
    try {
        s = new Scanner(new File("foo.json"));
        s.useDelimiter("\\Z");
        engine.getBindings(ScriptContext.GLOBAL_SCOPE).put("json", s.next());
    } finally {
        if (s != null) {
            s.close();
        }
    }
    // The default JavaScript engine in Java 6 does not have JSON.parse
    // but eval('(' + json + ')') would work
    val = engine.eval("JSON.parse(json)");

    // The default value is probably a JavaScript internal object and not very useful
    System.out.println(val.getClass() + " = " + val);
    // Java 7 uses Rhino 1.7R3 the objects will implement Map or List where appropriate
    // So in Java 7 we can turn this into something a little more useable
    // This is where Java 6 breaks down, in Java 6 you would have to use the 
    // sun.org.mozilla.javascript.internal classes to get any useful data
    System.out.println(convert(val));
}

@SuppressWarnings("unchecked")
public static Object convert(final Object val) {
    if (val instanceof Map) {
        final Map<String, Object> result = new HashMap<String, Object>();
        for (final Map.Entry<String, Object> entry: ((Map<String, Object>) val).entrySet()) {
            result.put(entry.getKey(), convert(entry.getValue()));
        }
        return result;
    } else if (val instanceof List) {
        final List<Object> result = new ArrayList<Object>();
        for (final Object item: ((List<Object>) val)) {
            result.add(convert(item));
        }
        return result;
    }
    if (val != null) {
        System.out.println(val.getClass() + " = " + val);
    }
    return val;
}

Java で提供される JavaScript エンジンは、Rhino で提供される機能の一部を除外しています。readFile 関数は、エンジンの実装ではなく、Rhino Shellによって実際に提供されます。Oracleは、エンジンにはないjrunscriptシェルからアクセスできる機能も提供します。

Rhino シェルの readFile 関数を模倣し、この質問への回答に基づいて JavaScript スコープに追加する例を次に示します: How can I add methods from a Java class as global functions in Javascript using Rhino?

public static void main(final String args[]) throws Exception {
    final ScriptEngineManager factory = new ScriptEngineManager();
    final ScriptEngine engine = factory.getEngineByName("js");
    engine.getBindings(ScriptContext.GLOBAL_SCOPE).put("utils", new Utils());
    engine.eval("for(var fn in utils) {\r\n" +
            "  if(typeof utils[fn] === 'function') {\r\n" +
            "    this[fn] = (function() {\r\n" +
            "       var method = utils[fn];\r\n" +
            "       return function() {\r\n" +
            "         return method.apply(utils,arguments);\r\n" +
            "       };\r\n" +
            "    })();\r\n" +
            "  }\r\n" +
            "}");
    engine.eval("println(readFile('foo.json'))");
}

static class Utils {
    public String readFile(final String fileName) throws FileNotFoundException {
        return readFile(fileName, null);
    }
    public String readFile(final String fileName, final String encoding) throws FileNotFoundException {
        Scanner s = null;
        try {
            s = new Scanner(new File(fileName), (encoding == null)? Charset.defaultCharset().name(): encoding);
            s.useDelimiter("\\Z");
            return s.next();
        } finally {
            if (s != null) {
                s.close();
            }
        }
    }
}
于 2012-04-13T18:51:41.727 に答える
2

関連するJavaクラスをいつでも活用してJSONファイルを丸呑みし、eval()に適したように装飾することができます。

1行で綺麗にできます。たとえば、次のようになります。

cfg.json:

{     
    myopts: {
        username: "tee",
        password: "password"
    }
}

そして、次の Javascript:

cfg = eval('(' + new java.util.Scanner( new java.io.File('cfg.json') ).useDelimiter("\\A").next() + ')');

次の方法で、Javascript で JSON にアクセスできるようになりました。

log.info("Username: " + cfg.myopts.username ); // "Username: tee"
于 2012-12-05T21:22:53.740 に答える