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"
}
精彩评论