Doc Holiday
Doc Holiday

Reputation: 10254

javax.servlet.ServletException: javax.servlet.jsp.JspTagException: Don't know how to iterate over supplied "items" in <forEach>

I have a Bean that holds the results. I need to use JSTL to iterate over it and present the results. Here is the bean:

public class DetResults
{
    private List<String> headings;
    private List<Class<?>> types;
    private List<Object[]> data;

    public DetResults() {}

    public List<String> getHeadings() { return this.headings; }
    public String getHeading(int which) { return this.headings.get(which); }

    public List<Class<?>> getTypes() { return this.types; }
    public Class<?> getType(int which) { return this.types.get(which); }

    public List<Object[]> getData( ) { return this.data; }
    public Object[] getDataAtRow( int row ) { return this.data.get(row); }


    public void setHeadings( List<String> val ) { this.headings = val; }
    public void setHeadings( String[] val ) { this.headings = Arrays.asList(val); }
    public void addHeading( String val ) 
    { 
        if( this.headings == null ) this.headings = new ArrayList<String>();
        this.headings.add(val); 
    }

    public void setTypes( List<Class<?>> val ) { this.types = val; }
    public void setTypes( Class<?> val[] ) { this.types = Arrays.asList(val); }
    public void addType( Class<?> val ) 
    {
        if( this.types == null ) this.types = new ArrayList<Class<?>>();
        this.types.add(val); 
    }


    public void setData( List<Object[]> val ) { this.data = val; }

    // allow NPE to get thrown
    public void setDataAtRow( Object[] val, int row ) { this.data.set(row, val); }

    public void appendDataRow( Object[] val ) 
    {
        if( data == null ) data = new ArrayList<Object[]>(); 
        this.data.add(val); 
    }

    public int getColumnCount() { return this.headings!=null?this.headings.size():0; }

}

Here is the handler that will set the bean to the JSP:

DetResults results = detDAO.fetchDetResults(paramBean);
request.setAttribute("results", results);
action.setJspURI(".../.jsp");

I tried to display it as follows:

<c:forEach var="results" items="${results}">
    ${results.heading}
</c:forEach>

But it threw the following exception:

Caused by: javax.servlet.ServletException: javax.servlet.jsp.JspTagException: Don't know how to iterate over supplied "items" in <forEach>

If I log the results on my handler page like this:

System.out.println( "\n\nthere are " + results.getColumnCount() + " columns in the result set" );
for( int i=0; i<results.getColumnCount(); i++ )
{
    System.out.println( results.getHeading(i) + " --> " + results.getType(i) );
}

The logging seems to show fine on the server.

Upvotes: 15

Views: 63339

Answers (3)

BalusC
BalusC

Reputation: 1109222

Caused by: javax.servlet.ServletException: javax.servlet.jsp.JspTagException: Don't know how to iterate over supplied "items" in <forEach>

That will happen when the <c:forEach items> does not refer a valid object over which it can iterate. The object should be an Object[] (a plain array), a Collection, Iterator, Enumeration, Map or String (see also source code). Anything else can't be iterated by <c:forEach>. Your DetResults class is not an instance of either of the aforementioned types, so it will fail.

Your DetResults class doesn't look right. It look basically like one God bean with a collection of all properties of multiple individual entities. This is not right. A bean class should represent at most one entity. Rewrite your DetResults class so that you basically end up with with a fullworthy collection of javabeans:

List<DetResult> results = detDAO.fetchDetResults(paramBean);

so that you can access it as follows:

<c:forEach items="${results}" var="result">
    ${result.heading}
    <c:forEach items="${result.data}" var="dataItem">
        ${dataItem}
    </c:forEach>
</c:forEach>

Note that I have fixed the <c:forEach var> attribute as well, it is not right to give it the same name as an existing object in the scope. It would only clash.

If you really insist to keep your DetResults bean as it is, you could access it as follows:

<c:forEach begin="0" end="${results.columnCount}" varStatus="loop">
    ${results.headings[loop.index]}
    <c:forEach items="${results.data[loop.index]}" var="dataItem">
        ${dataItem}
    </c:forEach>
 </c:forEach>

See also:

Upvotes: 34

Kanhaiya
Kanhaiya

Reputation: 1

Check this below code in servlet class:

request.setAttribute("productList", products);

And the same should match in JSP page in <c:forEach:

<c:forEach var= "tempProducts" items= "${productList}">

NOTE: "productList" name should match exactly in 1 point 1 and 2 then it will.

It worked for me.

Upvotes: -1

EMS
EMS

Reputation: 188

You should be able to iterate over the headings

<tr>
<c:foreach var="heading" items="${results.headings}">
  <th>${heading}</th>
</c:foreach>
</tr>

And then over the data...

<c:foreach var="row" items="${results.data}">
  <tr>
  <c:foreach var="i" items="${row}">
    <td>${i}</td>
  </c:foreach>
  </tr>
</c:foreach>

Or something along those lines?

Upvotes: 1

Related Questions