EL greift mit dem Integer-Schlüssel auf einen Kartenwert zu

85

Ich habe eine von Integer verschlüsselte Map. Wie kann ich mit EL über den Schlüssel auf einen Wert zugreifen?

Map<Integer, String> map = new HashMap<Integer, String>();
map.put(1, "One");
map.put(2, "Two");
map.put(3, "Three");

Ich dachte, das würde funktionieren, aber es funktioniert nicht (wo die Karte bereits in den Attributen der Anfrage enthalten ist):

<c:out value="${map[1]}"/>

Follow-up: Ich habe das Problem aufgespürt. Anscheinend ${name[1]}sucht eine Karte mit der Nummer als Long. Ich dachte , dieses, wenn ich geändert HashMapzu TreeMapund erhielt den Fehler:

java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.Long

Wenn ich meine Karte so ändere, dass sie:

Map<Long, String> map = new HashMap<Long, String>();
map.put(1L, "One");

gibt dann ${name[1]}"Eins" zurück. Was ist damit? Warum wird <c:out>eine Zahl als lang behandelt? Scheint mir nicht intuitiv zu sein (da int häufiger als long verwendet wird).

Meine neue Frage lautet also: Gibt es eine EL-Notation, um über einen IntegerWert auf eine Karte zuzugreifen ?

Steve Kuo
quelle

Antworten:

116

Erste Antwort (EL 2.1, Mai 2009)

Wie in diesem Java-Forum-Thread erwähnt :

Grundsätzlich fügt Autoboxing ein Integer-Objekt in die Map ein. dh:

map.put(new Integer(0), "myValue")

EL (Expressions Languages) wertet 0 als Long aus und sucht daher nach einem Long als Schlüssel in der Karte. dh es bewertet:

map.get(new Long(0))

Da a Longniemals einem IntegerObjekt entspricht, findet es den Eintrag in der Karte nicht.
Das war's auf den Punkt gebracht.


Update seit Mai 2009 (EL 2.2)

Im Dezember 2009 wurde EL 2.2 mit JSP 2.2 / Java EE 6 eingeführt , mit einigen Unterschieden zu EL 2.1 .
Es scheint (" EL Expression parsing integer as long "), dass:

Sie können die Methode intValuefür das LongObjekt self in EL 2.2 aufrufen :

<c:out value="${map[(1).intValue()]}"/>

Das könnte eine gute Abhilfe hier sein (auch in unten genannten Tobias Liefke ‚s Antwort )


Ursprüngliche Antwort:

EL verwendet die folgenden Wrapper:

Terms                  Description               Type
null                   null value.               -
123                    int value.                java.lang.Long
123.00                 real value.               java.lang.Double
"string" ou 'string'   string.                   java.lang.String
true or false          boolean.                  java.lang.Boolean

JSP-Seite, die dies demonstriert:

 <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>

 <%@ page import="java.util.*" %>

 <h2> Server Info</h2>
Server info = <%= application.getServerInfo() %> <br>
Servlet engine version = <%=  application.getMajorVersion() %>.<%= application.getMinorVersion() %><br>
Java version = <%= System.getProperty("java.vm.version") %><br>
<%
  Map map = new LinkedHashMap();
  map.put("2", "String(2)");
  map.put(new Integer(2), "Integer(2)");
  map.put(new Long(2), "Long(2)");
  map.put(42, "AutoBoxedNumber");

  pageContext.setAttribute("myMap", map);  
  Integer lifeInteger = new Integer(42);
  Long lifeLong = new Long(42);  
%>
  <h3>Looking up map in JSTL - integer vs long </h3>

  This page demonstrates how JSTL maps interact with different types used for keys in a map.
  Specifically the issue relates to autoboxing by java using map.put(1, "MyValue") and attempting to display it as ${myMap[1]}
  The map "myMap" consists of four entries with different keys: A String, an Integer, a Long and an entry put there by AutoBoxing Java 5 feature.       

  <table border="1">
    <tr><th>Key</th><th>value</th><th>Key Class</th></tr>
    <c:forEach var="entry" items="${myMap}" varStatus="status">
    <tr>      
      <td>${entry.key}</td>
      <td>${entry.value}</td>
      <td>${entry.key.class}</td>
    </tr>
    </c:forEach>
</table>

    <h4> Accessing the map</h4>    
    Evaluating: ${"${myMap['2']}"} = <c:out value="${myMap['2']}"/><br>
    Evaluating: ${"${myMap[2]}"}   = <c:out value="${myMap[2]}"/><br>    
    Evaluating: ${"${myMap[42]}"}   = <c:out value="${myMap[42]}"/><br>    

    <p>
    As you can see, the EL Expression for the literal number retrieves the value against the java.lang.Long entry in the map.
    Attempting to access the entry created by autoboxing fails because a Long is never equal to an Integer
    <p>

    lifeInteger = <%= lifeInteger %><br/>
    lifeLong = <%= lifeLong %><br/>
    lifeInteger.equals(lifeLong) : <%= lifeInteger.equals(lifeLong) %> <br>
VonC
quelle
Es gibt also keine Möglichkeit, EL dazu zu bringen, eine Zahl als Ganzzahl zu erweitern?
Steve Kuo
1
@Steve: In der Tat scheint EL das nicht zu unterstützen.
VonC
Ich habe diese Frage und Antwort aus einer Google-Suche gefunden. Sicher genug, sobald ich von Map <Integer, String> zu Map <Long, String> gewechselt bin, konnte ich mit der EL, die ich auf meiner JSP-Seite hatte, daraus ziehen. Vielen Dank!!
John Munsch
@Steve: Es ist möglich - siehe meine Antwort
Tobias Liefke
@SteveKuo Es sollte tatsächlich möglich sein. Mir ist jetzt klar, dass diese 6 Jahre alte Antwort geschrieben wurde, als EL 2.2 noch nicht veröffentlicht wurde. Ich habe diese Antwort bearbeitet und aktualisiert.
VonC
11

Ein weiterer hilfreicher Hinweis zusätzlich zu dem obigen Kommentar wäre, wenn in einer Variablen wie einem Anforderungsparameter ein Zeichenfolgenwert enthalten ist. In diesem Fall führt die Übergabe auch dazu, dass JSTL den Wert von "1" als Stich eingibt und daher keine Übereinstimmung in einer Map-Hashmap gefunden wird.

Eine Möglichkeit, dies zu umgehen, besteht darin, so etwas zu tun.

<c:set var="longKey" value="${param.selectedIndex + 0}"/>

Dies wird nun als langes Objekt behandelt und hat dann die Möglichkeit, ein Objekt abzugleichen, wenn es in der Kartenkarte oder was auch immer enthalten ist.

Fahren Sie dann wie gewohnt mit so etwas fort

${map[longKey]}
Dave
quelle
10

Sie können alle Funktionen von Long verwenden, wenn Sie die Nummer in "(" ")" eingeben. Auf diese Weise können Sie das Long in ein Int umwandeln:

<c:out value="${map[(1).intValue()]}"/>
Tobias Liefke
quelle
Ich habe Ihre Antwort nicht sofort gesehen. +1. Ich habe diese Möglichkeit für mehr Sichtbarkeit in meine Antwort aufgenommen und dokumentiert.
VonC
3

Basierend auf dem obigen Beitrag habe ich dies versucht und dies hat gut funktioniert. Ich wollte den Wert von Karte B als Schlüssel für Karte A verwenden:

<c:if test="${not empty activityCodeMap and not empty activityDescMap}">
<c:forEach var="valueMap" items="${auditMap}">
<tr>
<td class="activity_white"><c:out value="${activityCodeMap[valueMap.value.activityCode]}"/></td>
<td class="activity_white"><c:out value="${activityDescMap[valueMap.value.activityDescCode]}"/></td>
<td class="activity_white">${valueMap.value.dateTime}</td>
</tr>
</c:forEach>
</c:if>
Dhanashri
quelle
3

Wenn Sie zufällig eine Mapmit IntegerSchlüsseln haben, die Sie nicht ändern können, können Sie eine benutzerdefinierte EL-Funktion schreiben , um eine Longin zu konvertieren Integer. Dies würde Ihnen erlauben, etwas zu tun wie:

<c:out value="${map[myLib:longToInteger(1)]}"/>
Jasper de Vries
quelle