How to render arraylist of hashmaps in JSP page with STRUTS

Discussions

Web tier: servlets, JSP, Web frameworks: How to render arraylist of hashmaps in JSP page with STRUTS

  1. Hello,

    I just wonder whether there is a way to render an action form with a property with the type - ArrayList of hashmaps in JSP page? I try different combination of tags, still can't figure it out. If anyone can confirm this is not doable, it is also highly appreciated.

    Please see the code following:

    Here is my action class:

    package strutsTutorial.person2;

    import java.io.IOException;
    import java.util.HashMap;
    import javax.servlet.ServletException;
    import javax.servlet.http.*;
    import org.apache.struts.action.*;
    import org.apache.struts.validator.DynaValidatorForm;
    import strutsTutorial.person.*;
    import java.util.*;
    public class SetupHashFormAction extends Action {
    public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
    { DynaValidatorForm df = (DynaValidatorForm) form;
    ArrayList people = (ArrayList) df.get("people");

    HashMap person = new HashMap();
    person.put("dateOfBirth", "07/13/1942");
    person.put("lastName", "Ford");
    person.put("firstName", "Harrison");
    person.put("gender", "M");
    people.add(person);

    HashMap person2 = new HashMap();
    person2.put("dateOfBirth", "10/21/1956");
    person2.put("lastName", "Fisher");
    person2.put("firstName", "Carrie");
    person2.put("gender", "F");
    people.add(person2);

    HashMap person3 = new HashMap();
    person3.put("dateOfBirth", "09/25/1951");
    person3.put("lastName", "Hamill");
    person3.put("firstName", "Mark");
    person3.put("gender", "M");
    people.add(person3);


    return mapping.findForward("success"); }}

    Here is my struts configuration file:

    <?xml version="1.0" encoding="ISO-8859-1" ?>

    <!DOCTYPE struts-config PUBLIC
              "-//Apache Software Foundation//DTD Struts Configuration 1.1//EN"
              "http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd">



    <!--

         This is a Struts configuration file for the order module

         welcome action/page and other commented sample elements.



         Tiles and the Struts Validator are configured using the factory defaults

         and are ready-to-use.



         NOTE: If you have a generator tool to create the corresponding Java classes

         for you, you could include the details in the "form-bean" declarations.

         Otherwise, you would only define the "form-bean" element itself, with the

         corresponding "name" and "type" attributes, as shown here.

    -->





    <struts-config>



    <!-- ==================================== Data Source Configuration -->

    <!--type="org.apache.commons.dbcp.BasicDataSource" please never define this data source type unnecessarily-->







    <!-- ======================================== Form Bean Definitions -->



        <form-beans>
     
     <!--form-bean example to show Indexed Bean Properties with hashmaps-->



    <form-bean name="dynamicHashmapForm" type="org.apache.struts.validator.DynaValidatorForm">
    <form-property name="people" type="java.util.ArrayList"/>
    <form-property name="comments" type="java.util.HashMap"/>
    </form-bean>
     


        <!-- sample form bean descriptor for an ActionForm

            <form-bean

                name="inputForm"

                type="app.InputForm"/>

        end sample -->



        <!-- sample form bean descriptor for a DynaActionForm

            <form-bean

                name="logonForm"

                type="org.apache.struts.action.DynaActionForm">

                <form-property

                    name="username"

                    type="java.lang.String"/>

                <form-property

                    name="password"

                    type="java.lang.String"/> </form-bean>

        end sample -->



        </form-beans>





    <!-- ================================= Global Exception Definitions -->



        <global-exceptions>

            <!-- sample exception handler

            <exception

                key="expired.password"

                type="app.ExpiredPasswordException"

                path="/changePassword.jsp"/>

            end sample -->

        </global-exceptions>





    <!-- =================================== Global Forward Definitions -->

       <!--

        <global-forwards>

            
            <forward

                name="welcome"

                path="/Welcome.do"/>

        </global-forwards>-->





    <!-- =================================== Action Mapping Definitions -->



        <action-mappings>



                <!-- Default "Welcome" action -->

                <!-- Forwards to Welcome.jsp -->

           
             
             <!-- actions for the order application for indexed bean properties with Hashmaps -->
            
             <action path="/generateBeanPOForm" forward="/purchaseOrderBean.jsp"
    name="purchaseOrderBeanForm" scope="request"/>
    <action path="/generateBeanPO" type="strutsTutorial.order.GenerateBeanPO"
    name="purchaseOrderBeanForm" scope="request" input="/purchaseOrderBean.jsp">
    <forward name="success" path="/displayPurchaseOrderBean.jsp" redirect="false"/>



                   </action>
                    <!-- actions for the person application for Dynamic Sized indexed bean properties -->
    <action path="/setupHashForm" type="strutsTutorial.person2.SetupHashFormAction" name="dynamicHashmapForm"
    validate="false"> <forward name="success" path="/displayForm.jsp"/> </action>
    <action path="/processHashActorComments"
    type="strutsTutorial.person2.ProcessHashFormAction"
    name="dynamicHashmapForm"
    validate="false"> <forward name="success" path="/displayForm.jsp"/> </action>
                   
                   
                   <action path="/toModule"
    type="org.apache.struts.actions.SwitchAction"/>
             
        <!-- sample input and input submit actions



            <action

                path="/Input"

                type="org.apache.struts.actions.ForwardAction"

                parameter="/pages/Input.jsp"/>



            <action

                path="/InputSubmit"

                type="app.InputAction"

                name="inputForm"

                scope="request"

                validate="true"

                input="/pages/Input.jsp"/>
                
            



        end samples -->





        </action-mappings>





    <!-- ===================================== Controller Configuration -->



        <!--<controller

           processorClass="org.apache.struts.tiles.TilesRequestProcessor"/>-->





    <!-- ================================ Message Resources Definitions -->



        <message-resources parameter="resources.order"/>





    <!-- ======================================= Plug Ins Configuration -->


      <!-- ========== Tiles plugin =================== -->
      <!-- -->
      <!--
         This plugin initialize Tiles definition factory. This later can takes some
    parameters explained here after. The plugin first read parameters from web.xml, then
    overload them with parameters defined here. All parameters are optional.
    The plugin should be declared in each struts-config file.
           - definitions-config: (optional)
                Specify configuration file names. There can be several comma
    separated file names (default: ?? )
           - moduleAware: (optional - struts1.1)
                Specify if the Tiles definition factory is module aware. If true (default),
    there will be one factory for each Struts module.
    If false, there will be one common factory for all module. In this later case,
    it is still needed to declare one plugin per module. The factory will be
    initialized with parameters found in the first initialized plugin (generally the
    one associated with the default module).
    true : One factory per module. (default)
    false : one single shared factory for all modules
    - definitions-parser-validate: (optional)
    Specify if xml parser should validate the Tiles configuration file.
    true : validate. DTD should be specified in file header. (default)
    false : no validation

    Paths found in Tiles definitions are relative to the main context.
      -->
      <!-- comment following if struts1.0.x -->
      <plug-in className="org.apache.struts.tiles.TilesPlugin" >
        <set-property property="definitions-config"
    value="/WEB-INF/tiles-defs.xml" />
        <set-property property="moduleAware" value="true" />
        <set-property property="definitions-parser-validate" value="true" />
      </plug-in>

      <!-- end comment if struts1.0.x -->

      <plug-in className="org.apache.struts.validator.ValidatorPlugIn">
        <set-property
            property="pathnames"
            value="/WEB-INF/validator-rules.xml,/WEB-INF/validation.xml"/>
      </plug-in>

    </struts-config>

    Here is the jsp page with the problem:

    <%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
    <%@ taglib uri="/WEB-INF/struts-html-el.tld" prefix="html-el" %>
    <%@ taglib uri="/WEB-INF/struts-tiles.tld" prefix="tiles" %>
    <%@ taglib uri="/WEB-INF/c.tld" prefix="c" %>
    <%@ taglib prefix="fmt" uri="/WEB-INF/fmt.tld" %>
    <head><title>Star Wars Actor Fact Page</title></head>

    <html:form action="/processHashActorComments" >
    <table border="1" width="80%">
    <tr><th>Last Name</th><th>First Name</th>
    <th>Date of Birth</th><th>Comment</th>
    </tr> <c:forEach var="people" items="${dynamicHashmapForm.map.people}"> <tr><td><c:out value="${people.value.person.value.lastName}"/></td>
    <td><c:out value="${people.value.person.value.firstName}"/></td>
    <td><c:out value="${people.value.person.value.dateOfBirth}"/></td>
    <td><html-el:text property="comments(${people.value.person.value.lastName},${people.value.person.value.firstName})" />
    </td>
    </tr>
    </c:forEach>
    </table>
    </P>
    <html:submit value="Update Comments"/>
    </html:form>

    <html:link
    page="/toModule.do?prefix=&page=/Welcome.do">
    Back to the index page for the default module.
    </html:link>


    Thanks a lot for your help and have a great day,

    Jiao
  2. I didn't get more than about 20 lines in yet, but it doesn't look like your action ever adds the ArrayList to the request.
  3. This is the correct answer. You need to add the value to the request scope before you can render it on the jsp page.

    Add this line after you define your people ArrayList..
    request.setAttribute("peopleList", people);

    Then you can refernece the value ${peopleList} using JSTL tags, specifically the forEach tag. This will print a list of first names.

      <c:forEach items="${peopleList}" var="person">
    • <c:out value='${person["firstName"]}'/>
    • </c:forEach>

    As a side note, its considered better style to use the interfaces instead of the concrete classes when dealing with Collections.

    So intead of this...
    HashMap person2 = new HashMap();
    Use this..
    Map person2 = new HashMap();
    You don't really care if person2 is implemented as a HashMap() or ArrayMap() except when the object is actually created. After that its just a Map.
  4. David and Rich,

    Thanks a lot for giving your comments and suggestions.

    1) You guys might not notice that this arraylist of hashmaps is a property of the actionform, so I get the arraylist from the actionform, update it im my action, and I don't need to save it to request it again, since the actionform will be persistent in request.

    2) The tags David suggested is exactly what I am looking for and after I update my JSP accordingly, it works great, thank you. ;-)
    I know how to use the forEach tag to render an array of beans or a hashmap of beans, but I don't know how to use it to render an arralylist of hashmaps, this really helps a lot. I spend a couple of hours working on it without any luck.
    One thing catch me is using the single quote around the doulbe quote, if you don't show me the example, I can never figure it out by my self. Although it is a common practice for Java String manipulation.

    3)For the MAP vs HASHMAP suggestion, I really enjoy it, since one of my teammates just suggest something similar to me today. Great minds think alike.

    I will adopt this as a general practice, since it does leaves a lot of flexibility for possible changes.

    Thanks a lot and have a great day,

    Jiao
  5. I think You need to use something as :

    <%
    Iterator i=list.iterator();
    while( i.hasNext() )
      {
      //String comp=(String)i.next();
      HasMap map=(hashMap)i.next();
    %>

    and retrieve your data