开发者

Accessing java Maps & Lists as JavaScript Objects in Rhino

开发者 https://www.devze.com 2023-03-06 23:56 出处:网络
Is there a way to access Java Maps and Lists as JavaScript Objects in Rhino? I have a Map which contains only other maps and lists of primitives and Strings, I\'d like to pass this to a Rhino script

Is there a way to access Java Maps and Lists as JavaScript Objects in Rhino?

I have a Map which contains only other maps and lists of primitives and Strings, I'd like to pass this to a Rhino script and do stuff to it, and return the modified object back out to Java - but since they are java.util.Map and java.util.List Objects,开发者_JAVA百科 I can't use standard JavaScript associative array syntax. ie: fooMap.get("keyName") will work, but fooMap.keyName and fooMap["keyName"] will not.

I don't know if there is a Rhino-specific way to do this, or if there is some conversion/cast utility that will help. Commons BeanUtils is not sufficient, because to convert a Map to a bean (which can be accessed via associative array syntax), you must first create a class which has all of the named mutators/accessors. I won't know the structure of the object at runtime.


Take a look at RingoJS. It has convenient wrappers for list and map for Rhino such as this one


iterators seem to be the key!

if you want to iterate over all entries of a map, you could do the following

JAVA

//pass  the map and map.keySet().iterator() to the javascript
Object wrappedParameterMap = Context.javaToJS(parameterMap, scope);
ScriptableObject.putProperty(scope, "parameterMap", wrappedParameterMap);
Object wrappedParameterNames = Context.javaToJS(parameterMap.keySet().iterator(), scope);
ScriptableObject.putProperty(scope, "parameterNames", wrappedParameterNames);

JAVASCRIPT

while(parameterNames.hasNext()) {
  key = parameterNames.next();
  value = parameterMap.get(key);
}


https://developer.mozilla.org/en/New_in_Rhino_1.7R3#JS.c2.a0Objects_implement_Java_collections claims that JS objects resp. arrays can now be cast to Map resp. List, but this does not seem like it would work for your use case.


I had a similar problem that may be useful. I tried created native rhino objects and copied data into them.


Since Rhino 1.7.12 you can create ES6 lambdas and therefore create your own utility function like this:

function iterateMap(map, callback) {
    var iter = map.keySet().iterator();
    while(iter.hasNext()) {
        var key = iter.next();
        var value = map.get(key);
        callback(key, value);
    }
}

And then iterate like this

iterateMap(map, (key, value)=>{
    //...
});


The way to achieve this since Rhino 1.7.14 is to enable Context.FEATURE_ENABLE_JAVA_MAP_ACCESS.

This requires implementing a custom ContextFactory:

public class MyContextFactory extends ContextFactory {
    @Override
    protected boolean hasFeature(Context cx, int featureIndex) {
        switch (featureIndex) {
            case Context.FEATURE_ENABLE_JAVA_MAP_ACCESS:
                return true;
        }
        return super.hasFeature(cx, featureIndex);
    }
}

Then, this factory can be used to instantiate contexts that has this feature enabled:

Map<String, String> myMap = Map.of("message", "Hello from JavaScript");

ContextFactory ctxFactory = new MyContextFactory();
try (Context ctx = ctxFactory.enterContext()) {
    Scriptable scope = ctx.initSafeStandardObjects();
    ScriptableObject.putProperty(scope, "myMap", Context.javaToJS(myMap, scope));
    
    // Now map properties are accessible via `[key]` or `.key`:
    Object result = ctx.evaluateString(scope, "myMap.message", "<sample>", 1, null);
    String message = Context.jsToJava(result, String.class);
    System.out.println(message); // prints "Hello from JavaScript"
}
0

精彩评论

暂无评论...
验证码 换一张
取 消