2010-08-13 12 views
10

Widzę dziwne zachowanie w implementacji mapy javax.scripting.Importowanie mapy w javax.scripting javascript

Przykłady forum pokazać example dodawania do listy w środowisku JS:

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); 

Jeśli jednak spróbować zrobić coś podobnego z mapą, widać dziwne zachowanie. Po pierwsze, jeśli próbujesz iterować za pomocą klawiszy mapy, np.

HashMap<String, Object> m = new HashMap<String, Object>(); 
jsEngine.put("map", m); 

Nie ma sposobu, aby uzyskać klucze mapie - jeśli spróbujesz iterację kluczy, otrzymasz metodę names-

jsEngine.eval(" for (var k in m.keySet()){ println(k)};"); 

skutkuje:

notifyAll 
removeAll 
containsAll 
contains 
empty 
equals 
... 

w js Kontekst można adres wartości na mapie z m.get(key), ale nie z m[key], a jeśli klucz nie istnieje, zgłasza błąd. Czy ktoś może rzucić trochę światła na to zachowanie, czy jest po prostu zepsuty? Dzięki.

+0

To bardzo interesujące. Może to być iteracja nad obiektami keySet (w Rhino możesz zapętlać metody), ale nie jestem pewien. Będę musiał to wypróbować – TheLQ

Odpowiedz

12

for..in w JavaScript to nie to samo co dla ..each w Javie, nawet jeśli wyglądają podobnie. for..in w JavaScript iteruje po nazwach właściwości w obiekcie. Nazwy metod są narażone na Rhino jako właściwości na rodzimym obiektu Java HashMap, tak jakby miał następujący obiekt JavaScript:

{ 
notifyAll:function(){}, 
removeAll:function(){}, 
containsAll:function(){}, 
contains:function(){}, 
empty:function(){}, 
equals:function(){} 
} 

Moja rada jest taka, że ​​albo przekształcić zestaw kluczy HashMap do tablicy za pomocą metody określonej .toArray, lub otrzymujesz iterator za pomocą Set.iterator(). Oto krótki skrypt Rhino pokazując, jak można to osiągnąć stosując metodę ToArray:

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); 
} 

które wyjścia:

bif 
bar 
boo 

Oto jak można zrobić to samo przy użyciu 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); 
} 
+0

Dzięki, dokładny i użyteczny. –

2

Po przekonwertowaniu pliku java.util.Map na obiekt natywny JavaScript będzie czystszy:

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');" 
); 

W przeciwnym razie utknąłeś ze standardową składnią Java.

+0

Niestety, twój NativeObject musi być albo Rhino albo Nashorn API. Nie istnieje w typowym interfejsie API javax.script. – jfrantzius