EL accède à une valeur de carte par clé Integer

85

J'ai une carte saisie par Integer. En utilisant EL, comment puis-je accéder à une valeur par sa clé?

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

Je pensais que cela fonctionnerait mais ce n'est pas le cas (où la carte est déjà dans les attributs de la demande):

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

Suivi: j'ai retracé le problème. Apparemment, ${name[1]}fait une recherche sur la carte avec le numéro en tant que Long. Je compris cela quand j'ai changé HashMapà TreeMapet reçu l'erreur:

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

Si je change ma carte pour être:

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

puis ${name[1]}renvoie "Un". Qu'est-ce que c'est que ça? Pourquoi <c:out>traite un nombre comme un long. Cela me semble contre-intuitif (car int est plus couramment utilisé que long).

Ma nouvelle question est donc: y a-t-il une notation EL pour accéder à une carte par une Integervaleur?

Steve Kuo
la source

Réponses:

117

Réponse initiale (EL 2.1, mai 2009)

Comme mentionné dans ce fil de discussion du forum java :

Fondamentalement, l'autoboxing place un objet Integer dans la carte. c'est à dire:

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

EL (Expressions Languages) évalue 0 comme un Long et cherche donc un Long comme clé dans la carte. c'est-à-dire qu'il évalue:

map.get(new Long(0))

Comme a Longn'est jamais égal à un Integerobjet, il ne trouve pas l'entrée dans la carte.
C'est tout en un mot.


Mise à jour depuis mai 2009 (EL 2.2)

Décembre 2009 a vu l'introduction d'EL 2.2 avec JSP 2.2 / Java EE 6 , avec quelques différences par rapport à EL 2.1 .
Il semble (" EL Expression parsing integer as long ") que:

vous pouvez appeler la méthode intValuesur l' Longobjet self dans EL 2.2 :

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

Cela pourrait être une bonne solution de contournement ici (également mentionné ci-dessous dans la réponse de Tobias Liefke )


Réponse originale:

EL utilise les wrappers suivants:

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

Page JSP démontrant ceci:

 <%@ 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
la source
Il n'y a donc aucun moyen d'amener EL à étendre un nombre en tant qu'entier?
Steve Kuo
1
@Steve: en effet, EL ne semble pas soutenir cela.
VonC
J'ai trouvé cette question et réponse à partir d'une recherche Google. Effectivement, dès que je suis passé de Map <Integer, String> à Map <Long, String>, j'ai pu en extraire en utilisant l'EL que j'avais dans ma page JSP. Merci!!
John Munsch
@Steve: C'est possible - voir ma réponse
Tobias Liefke
@SteveKuo Cela devrait en effet être possible. Je me rends compte maintenant que cette réponse vieille de 6 ans a été écrite alors que EL 2.2 n'était pas encore disponible. J'ai édité et mis à jour ladite réponse.
VonC
11

Un autre conseil utile en plus du commentaire ci-dessus serait lorsque vous avez une valeur de chaîne contenue dans une variable telle qu'un paramètre de requête. Dans ce cas, le passage de ceci entraînera également la saisie par JSTL de la valeur de disons "1" comme une piqûre et, en tant que telle, aucune correspondance ne sera trouvée dans un hashmap de carte.

Une façon de contourner ce problème est de faire quelque chose comme ça.

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

Celui-ci sera désormais traité comme un objet Long et aura ensuite une chance de correspondre à un objet lorsqu'il est contenu dans la carte Map ou autre.

Ensuite, continuez comme d'habitude avec quelque chose comme

${map[longKey]}
Dave
la source
10

Vous pouvez utiliser toutes les fonctions de Long, si vous mettez le nombre dans "(" ")". De cette façon, vous pouvez convertir le long en un entier:

<c:out value="${map[(1).intValue()]}"/>
Tobias Liefke
la source
Je n'ai pas vu votre réponse immédiatement. +1. J'ai inclus et documenté cette possibilité dans ma réponse pour plus de visibilité.
VonC
3

Sur la base du post ci-dessus, j'ai essayé ceci et cela a bien fonctionné, je voulais utiliser la valeur de la carte B comme clés pour la carte A:

<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
la source
3

Si vous venez tout juste d'avoir Mapavec les Integerclés que vous ne pouvez pas changer, vous pouvez écrire une fonction EL personnalisé pour convertir un Longà Integer. Cela vous permettrait de faire quelque chose comme:

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