JSTL: getting a map's keys

Discussion in 'Java' started by Chris Riesbeck, Mar 19, 2012.

  1. Can anyone help me figure out why the following is happening, or further
    experiments I can run? (I have workarounds but I'd like to know what I'm

    - SimTable, a subclass of HashTable that defines getKeys() to call
    - a session variable "data" which is a HashTable with "rates" set to
    an instance of a SimTable

    Why does ${rates.keys} generate nothing in these lines of JSP?

    <c:set var="rates" value='${data["rates"]}'/>

    <p>rates: ${rates}</p>

    <p>jstl: ${rates.keys}</p>

    <p>jsp: <%= ((edu.northwestern.ils.simulator.SimTable)
    pageContext.findAttribute("rates")).getKeys() %></p>

    Here's the generated HTML output:

    <p>rates: {Bill=100.0, Fred=75.0, Jane=75.0, Mary=50.0, Sam=75.0,

    <p>jstl: </p>

    <p>jsp: [Bill, Fred, Jane, Mary, Sam, Sarah]</p>
    Chris Riesbeck, Mar 19, 2012
    1. Advertisements

  2. Chris Riesbeck

    Lew Guest

    If you mean 'java.util.Hashtable', that's a rather obsolete class. You should stick with 'java.util.HashMap'.
    Joshua Bloch recommends to prefer composition over inheritance.
    How about you follow the advice here:
    and then maybe we can help you. It would make it easier for you to answer the questions I'm asking.

    I think perhaps you're overthinking the session vars. Why can't your 'rates' Map go in there directly?
    Your example code is fragmented and incomplete. Give us something we can work with, please.
    Lew, Mar 19, 2012
    1. Advertisements

  3. Chris Riesbeck

    Tim Slattery Guest

    The EL here finds the object "rates" and looks for an attribute named
    "keys". That means that if rates doesn't have a public method named
    getKeys, the EL won't find anything.
    Tim Slattery, Mar 19, 2012
  4. Which it does. Here's a complete example, class and test JSP, and the
    HTML output I get

    ****** CLASS

    package example;

    import java.util.HashMap;
    import java.util.Set;

    public class SimTable extends HashMap<String, Object> {

    public Set<String> getKeys() {
    return keySet();

    ****** JSP

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

    final example.SimTable data = new example.SimTable();
    data.put("Bill", 100); data.put("Mary", 150); data.put("Fred", 200);
    pageContext.setAttribute("data", data);

    <!doctype HTML>
    <p>data: ${data}</p>
    <p>data.keys: ${data.keys}</p>
    <%= ((example.SimTable) pageContext.findAttribute("data")).getKeys() %>

    ****** HTML output

    <!doctype HTML>
    <p>data: {Bill=100, Mary=150, Fred=200}</p>
    <p>data.keys: </p>
    [Bill, Mary, Fred]

    Chris Riesbeck, Mar 19, 2012
  5. And I added these lines to my JSP to see if "keys" was appearing as a
    property via the Introspector and it was

    ****** additional JSP

    <c:set var="props"
    %>' />
    <c:forEach var="prop" items="${props}">

    ****** additional HTML output





    Chris Riesbeck, Mar 19, 2012
  6. Chris Riesbeck

    markspace Guest

    Up before the part you snipped, the OP mentioned that he did sub-class
    HashTable and add a "getKeys()" method.

    Hmm, maybe there's a clue there. Are there coercion rules for JSTL?
    It's been a long while since I looked at it. Is it possible that JSTL
    is coercing SimTable (the one with getKeys()) to a HashTable, which does
    not have "getKeys()" defined?

    You might want to uncompile the .class file for this JSTL, it might show
    you what is actually being produced. There may be a clue there.
    markspace, Mar 19, 2012
  7. Chris Riesbeck

    Daniel Pitts Guest

    My guess is that ${rates.keys} is interpreted as equivalent to
    ${rates['keys']}, so it is looking for a key of "keys", not a java bean

    You *can* use c:forEach to iterate over the entry set (Map.Entry) in a

    Hopefully this helps,
    Daniel Pitts, Mar 19, 2012
  8. Ah! This would explain why ${rates.class} doesn't work either.
    Basically, name.key will always be taken as name[key] for a Map, even if
    key is a property of name. If I'm reading the JSP EL resolver
    specification correctly, that's just what is happening.
    Yes, that's what I've been doing instead for looping.
    Indeed. Thanks, Daniel
    Chris Riesbeck, Mar 20, 2012
  9. Just to nail the coffin lid shut on this. The JSP EL defines name.key as
    just shorthand for name["key"]. To interpret [] expressions, JSP uses
    the first answer it gets from this chain of resolvers:

    registered custom ELResolvers


    So the Map interpretation will always override the Bean interpretation.
    Chris Riesbeck, Mar 20, 2012
  10. Chris Riesbeck

    Lew Guest

    Now that your main question is answered, a couple of comments are in order.

    - Don't have scriptlet in your JSPs.
    - If you had *composed* a 'Map' into a custom class rather than inheriting 'Map', you would not have had the problem. Your custom class would have been resolved by the bean resolver.
    - This in turn would make for a better design overall. Instead of your viewartifact (the JSP) caring about the implementation details of the map and its set of keys, you'd have a controller call like 'getKeys()' or whatever that would cleanly separate the logic of how you get them from presentationconcerns.
    Lew, Mar 20, 2012
  11. Chris Riesbeck

    Daniel Pitts Guest

    Another interesting side-effect of this approach is that
    SomeObject.property might attempt to parse "property" as a number if
    SomeObject implements List. We've had this problem where a generic class
    which converts XML->Maps/Lists/Strings used different types depending on
    the multiplicity of specific tags. (It was bad design IMO, but I was
    politically unable to have it designed otherwise).
    Daniel Pitts, Mar 20, 2012
  12. Chris Riesbeck

    markspace Guest

    markspace, Mar 20, 2012
  13. Chris Riesbeck

    Daniel Pitts Guest

    Yes, I actually new the "answer", but not the official "reason". It was
    interesting to see that "there's a spec for that".
    Daniel Pitts, Mar 20, 2012
  14. I never do. That was in the original JSP just to show that getKeys() was
    functioning and returning a non-empty result
    Using delegation (my preference also) doesn't support the JSP EL form
    ${rates["Bill"]}. If I had a custom class, I'd need a custom tag or
    custom EL resolver to make that work.

    The original goal was to get the JSTL away from knowing about Map's, in
    particular about entry.key and entry.value.
    Chris Riesbeck, Mar 21, 2012
  15. Chris Riesbeck

    Lew Guest

    No, you most certainly would not.

    Just define an appropriate method in your class.
    Which would be neatly accomplished by my suggestion.
    Lew, Mar 22, 2012
  16. Color me totally dense. Maybe I'm missing what method I should be defining.

    If my class implements the Map interface, then MAPElResolver will be
    invoked and do the same thing with name.key and name["key"]: call
    name.get("key"). The JSP EL won't be able to call any bean method of name.

    If my class doesn't implement Map, then BeanELResolver will be invoked
    and do the same thing with name.key and name["key"]: call name.getKey().
    The JSP EL won't be able to call name.get() via [].

    So I don't see how I can have a class such that in the JSP EL I can call
    a method to get all the keys, and also retrieve data with a key using [].
    Chris Riesbeck, Mar 22, 2012
  17. Chris Riesbeck

    Lew Guest

    I was thinking along the lines of this incompletely worked-through notion:

    public class ContactScreenBacker
    private final Map<String, Rate> rates = howeverYouSetItUp();
    public Map<String, Rate> getRates() { return Collections.unmodifiableMap(rates); }
    public Set<String> getKeys() { return Collections.unmodifiableSet(rates.getKeys(); }
    // ...

    You should be able to use an EL like '#{thingie.rates["Bill"]}'. I haven't tried it, but this is what I had in mind.
    Lew, Mar 22, 2012
  18. Yes, that's probably the approach I'll take. Not quite what I'd wanted,
    but close enough and I can't see why it shouldn't work.

    Thanks, all.
    Chris Riesbeck, Mar 23, 2012
  19. Chris Riesbeck

    Lew Guest

    Let us know how it plays out, please.
    Lew, Mar 23, 2012
  20. So far, fine. Only time will tell if it pays off in how my JSP looks.

    Chris Riesbeck, Mar 26, 2012
    1. Advertisements

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.