EL Map won't work with numerical index - works with string
If I populate a map with a numeric index, the values won't appear when I refer to it as ${myMap[0]} in JSP. In my servlet I populate myMap by doing a put(0,value).
However, when I do a myMap["index0"] or myMap.index0, it works! I simply change the line that populates the Map to put("index0",value).
Why won't it work with a numerical index? Does it have something to do with JDK 1.5's autoboxing? I cast the 0 to an int in the put( ) method, but that makes no difference.
What could be happening here? Does it work for you?
Thanks.
# 1
[nobr]I think what you might be getting here is the difference between java.lang.String and java.lang.Integer. The String "0" will never be equal with the Integer 0.
Just as an experiment, try the following code
<table>
<tr><th>Entry#</th><th>Key</th><th>value</th><th>Key Class</th></tr>
<c:forEach var="entry" items="${myMap}" varStatus="status">
<tr>
<td>${status.index}</td>
<td>${entry.key}</td>
<td>${entry.value}</td>
<td>${entry.key.class}</td>
</tr>
</c:forEach>
</table>
Accessing the map with Integer?: ${myMap[0]}<br>
Accessing the map with String: ${myMap['0']}<br>
<% pageContext.setAttribute("testInteger", new Integer(0)); %>
Accessing with a definite Integer ${myMap[testInteger]}" <br>
That should tell you if the key in the map is a java.lang.String, a java.lang.Integer or whatever else it might be.
And then tests the different types for the key: Integer and String.
Hope this helps,
evnafets[/nobr]
# 2
Thanks for your advice. I tried the tests you provided, and everything came up blank, apart from the ${status.index}. This tells me that it cannot see the map keys or values as anything - Strings, Integers, nothing at all.
What really surprises me is that it does show up when I append a String to the map's key and refer to it as such in the JSP, which tells me the rest of the code and JSP is not making an unrelated error.
What can I try from here? I'd just like to confirm that what I'm seeing is unexpected behavior, and that I should in fact be able to refer to it as ${myMap[0]} to get the value of a map entry with a key of integer 0?
# 3
You see nothing at all?
But you do see the status.index increasing?
If the index is increasing the map must be there in scope, and have entries in it. I have no idea why they would print no data though.
That sounds very strange to me.
How do you build your Map?
Can you show the code for your map plus putting it into a request/session attribute?
# 4
In the doGet method of my servlet is the following code (it's a List of Maps). This sample code doesn't work, however.
In the JSP, the number of entries in the List is always correctly outputted (and from the test code you suggested the index is increasing). However, the actual values within the Map are not.
List myList = new ArrayList();
myList.add(new HashMap().put(0, "firstMap"));
myList.add(new HashMap().put(0, "secondMap"));
request.setAttribute("myList", myList);
RequestDispatcher rd = request.getRequestDispatcher("/page.jsp");
rd.forward(request, response);
In the /page.jsp is the following:
<c:forEach items="${myList}" var="listItem">
${listItem[0]}
</c:forEach>
The ouput I'd expect is:
firstMap secondMap
# 5
Please ignore my previous post, and see this one instead. Below is a simple, self-contained JSP that demonstrates the problem.
This works as expected in its current form, with map.put("0","item"+i) and ${map["0"]} - note the use of "0" as a String as opposed to 0 as an int. If you try to change "0" to 0 in those two places in the following, you will see exactly what the problem is.
What's causing this? I thought my use of ${map[0]} is a simple and common use of the EL in JSP.
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page import="java.util.*" %>
<%
// THIS WORKS - USING STRING
List list = new ArrayList();
for (int i=0; i<10; i++) {
Map map = new HashMap();
map.put("0","item"+i);
list.add(map);
}
request.setAttribute("list",list);
%>
<c:forEach items="${list}" var="map">
[${map["0"]}]
</c:forEach>
<%
// THIS DOESN'T WORK - USING INT
list = new ArrayList();
for (int i=0; i<10; i++) {
Map map = new HashMap();
map.put(0,"item"+i);
list.add(map);
}
request.setAttribute("list",list);
%>
<c:forEach items="${list}" var="map">
[${map[0]}]
</c:forEach>
# 6
[nobr]Whats causing this?
Basically autoboxing puts an Integer object into the Map.
ie: map.put(new Integer(0), "myValue")
EL evaluates 0 as a Long and thus goes looking for a Long as the key in the map.
ie it evaluates: map.get(new Long(0))
As a Long is never equal to an Integer object, it does not find the entry in the map.
Thats it in a nutshell.
JSP page demonstrating this:
<%@ 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>
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
lifeInteger = <%= lifeInteger %><br/>
lifeLong = <%= lifeLong %><br/>
lifeInteger.equals(lifeLong) : <%= lifeInteger.equals(lifeLong) %> <br>
That at least explains what is going on.
Not sure exactly what I would do to "fix" it. Would have to look at what the requirements are. But basically I think it would end up as having to use Longs rather than Integers as keys in the map, meaning autoboxing could not be used.
Cheers,
evnafets[/nobr]
# 7
Absolutely brilliant - thank you!How did you figure that out? Is the fact that EL indexes expect longs in the EL specification (which, if yes, means shame on me for not reading it), or were you just trying things out?
# 8
Its a problem I've come across before - or very close to it.
I think the original issue I had was Struts related, and I put Integers in the Map, and struts was using Strings to look things up. So I vaguely knew what the cause of the issue could be and just needed to play a little bit to have it come out. The auto-boxing was a new twist on things.
# 9
I have a similar question as before:
If I add a HashMap entry where the value is an Object obtained by myJdbcResultSet.getObject(columnNum), will the value used by the EL be interpreted as an Object type (which is its reference type) or whatever the underlying database value returned is (e.g. Integer, String, etc)?
I ask because if using getObject is okay (meaning the EL works with this method), then I'll use a generic rs.getObject as opposed to having to check each value and do a rs.getString or rs.getInt as necessary.