问题
I'm seeing some odd behavior in the javax.scripting map implementation.
The online examples show an example of adding to a list within the js environment:
ScriptEngineManager mgr = new ScriptEngineManager();
ScriptEngine jsEngine = mgr.getEngineByName("JavaScript");
List<String> namesList = new ArrayList<String>();
namesList.add("Jill");
namesList.add("Bob");
namesList.add("Laureen");
namesList.add("Ed");
jsEngine.put("namesListKey", namesList);
System.out.println("Executing in script environment...");
try
{
jsEngine.eval("var names = namesListKey.toArray();" + "for(x in names) {" + " println(names[x]);" + "}"
+ "namesListKey.add(\"Dana\");");
} catch (ScriptException ex)
{
ex.printStackTrace();
}
System.out.println(namesList);
However, if you try to do something similar with a map, you see odd behavior. For one thing, if you try to iterate through the map keys, e.g.
HashMap<String, Object> m = new HashMap<String, Object>();
jsEngine.put("map", m);
There's no way to obtain the map keys - if you try to iterate through the keys, you get method names-
jsEngine.eval(" for (var k in m.keySet()){ println(k)};");
results in :
notifyAll
removeAll
containsAll
contains
empty
equals
...
In the js context you can address values in the map with m.get(key)
but not with m[key]
, and if the key doesn't exist it throws an error. Can anyone shed some light on this behavior, or is it just broken? Thanks.
回答1:
for..in in JavaScript is not the same thing as for..each in Java, even though they look similar. for..in in JavaScript iterates over the property names in an object. The method names are exposed to Rhino as properties on the native Java HashMap object, just as if you had the following JavaScript object:
{
notifyAll:function(){},
removeAll:function(){},
containsAll:function(){},
contains:function(){},
empty:function(){},
equals:function(){}
}
My recommendation is that you either convert the HashMap keyset to an array, using method Set.toArray, or you obtain an iterator using Set.iterator(). Here's a short Rhino script showing how you may accomplish this using the toArray method:
x=new java.util.HashMap();
x.put("foo","bar");
x.put("bat","bif");
x.put("barf","boo");
var keyArray = x.keySet().toArray();
for(var i=0, l = keyArray.length; i < l; i++){
var key = keyArray[i];
var value = x.get(key);
print(value);
}
Which outputs:
bif
bar
boo
Here's how you can do the same thing using Set.iterator:
x=new java.util.HashMap();
x.put("foo","bar");
x.put("bat","bif");
x.put("barf","boo");
var keyIter = x.keySet().iterator();
while(keyIter.hasNext()){
var key = keyIter.next()
var value = x.get(key);
print(value);
}
回答2:
If you convert java.util.Map to a native object, your JavaScript will be cleaner:
final Map<String,String> javaMap = new HashMap<>();
javaMap.put("alpha", "bravo");
javaMap.put("charlie", "delta");
final NativeObject jsMap = new NativeObject();
for (Map.Entry<String,String> entry : javaMap.entrySet()) {
jsMap.defineProperty(
entry.getKey(), entry.getValue(), NativeObject.READONLY
);
}
final ScriptEngine jsEngine =
(new ScriptEngineManager()).getEngineByName("JavaScript");
jsEngine.put("map", jsMap);
jsEngine.eval(
"for (var idx in map) print(idx + '; ' + map[idx] + '\\n');"
);
Otherwise, you're stuck with standard Java syntax.
来源:https://stackoverflow.com/questions/3479146/importing-a-map-in-javax-scripting-javascript-environment