EL accede a un valor de mapa mediante la tecla Integer

85

Tengo un mapa codificado por Integer. Usando EL, ¿cómo puedo acceder a un valor por su clave?

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

Pensé que esto funcionaría pero no funciona (donde el mapa ya está en los atributos de la solicitud):

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

Seguimiento: rastreé el problema. Aparentemente, ${name[1]}hace una búsqueda en el mapa con el número como Long. Me di cuenta de esto cuando cambié HashMapa TreeMapy recibió el error:

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

Si cambio mi mapa para que sea:

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

luego ${name[1]}devuelve "Uno". ¿Qué hay con eso? ¿Por qué <c:out>trata un número como largo? Me parece contrario a la intuición (ya que int se usa más comúnmente que long).

Entonces, mi nueva pregunta es, ¿hay una notación EL para acceder a un mapa por un Integervalor?

Steve Kuo
fuente

Respuestas:

117

Respuesta inicial (EL 2.1, mayo de 2009)

Como se menciona en este hilo del foro de Java :

Básicamente, el autoboxing coloca un objeto Integer en el mapa. es decir:

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

EL (Expressions Languages) evalúa 0 como Long y, por lo tanto, busca un Long como clave en el mapa. es decir, evalúa:

map.get(new Long(0))

Como a Longnunca es igual a un Integerobjeto, no encuentra la entrada en el mapa.
Eso es todo en pocas palabras.


Actualización desde mayo de 2009 (EL 2.2)

En diciembre de 2009 se introdujo EL 2.2 con JSP 2.2 / Java EE 6 , con algunas diferencias en comparación con EL 2.1 .
Parece (" EL Expression parsing integer as long ") que:

puede llamar al método intValueen el Longobjeto self dentro de EL 2.2 :

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

Esa podría ser una buena solución aquí (también se menciona a continuación en la respuesta de Tobias Liefke )


Respuesta original:

EL utiliza las siguientes envolturas:

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

Página JSP que demuestra esto:

 <%@ 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
fuente
Entonces, ¿no hay forma de que EL expanda un número como un entero?
Steve Kuo
1
@Steve: de hecho, EL no parece apoyar eso.
VonC
Encontré esta pregunta y respuesta de una búsqueda en Google. Efectivamente, tan pronto como cambié de Map <Integer, String> a Map <Long, String>, pude extraerlo usando el EL que tenía en mi página JSP. ¡¡Gracias!!
John Munsch
@Steve: Es posible - vea mi respuesta
Tobias Liefke
@SteveKuo Debería ser posible. Ahora me doy cuenta de que esta respuesta de hace 6 años se escribió cuando EL 2.2 aún no estaba disponible. He editado y actualizado dicha respuesta.
VonC
11

Solo otra pista útil además del comentario anterior sería cuando tenga un valor de cadena contenido en alguna variable, como un parámetro de solicitud. En este caso, pasar esto también dará como resultado que JSTL ingrese el valor de, por ejemplo, "1" como una picadura y, como tal, no se encontrará ninguna coincidencia en un mapa hash de mapa.

Una forma de evitar esto es hacer algo como esto.

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

Esto ahora se tratará como un objeto largo y luego tendrá la oportunidad de coincidir con un objeto cuando esté contenido dentro del mapa Mapa o lo que sea.

Luego, continúe como de costumbre con algo como

${map[longKey]}
Dave
fuente
10

Puede utilizar todas las funciones de Long, si pone el número en "(" ")". De esa manera, puede convertir el largo en un int:

<c:out value="${map[(1).intValue()]}"/>
Tobias Liefke
fuente
No vi tu respuesta de inmediato. +1. He incluido y documentado esa posibilidad en mi respuesta para una mayor visibilidad.
VonC
3

Basado en la publicación anterior, probé esto y funcionó bien, quería usar el valor del Mapa B como claves para el Mapa 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
fuente
3

Si tiene una Mapcon Integerteclas que no puede cambiar, puede escribir una función EL personalizada para convertir una Longa Integer. Esto le permitiría hacer algo como:

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