Jump to content

ClassCast Exception when converting single-element Collection to String


bill.reynolds
Go to solution Solved by lucianc,

Recommended Posts

Hi, 


I'm running into an issue running the following JRXML file. Here's what I'm trying to do - I'd like to pass this report a collection of strings, and then join these strings into a new String parameter using the expression editor. 

Here is the JRXML file:

 

<?xml version="1.0" encoding="UTF-8"?>

<!-- Created with Jaspersoft Studio version 6.3.0.final using JasperReports Library version 6.3.0  -->

<!-- 2017-11-13T15:34:24 -->

<jasperReport xmlns="http://jasperreports.sourceforge.net/jasperreports" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://jasperreports.sourceforge.net/jasperreports http://jasperreports.sourceforge.net/xsd/jasperreport.xsd" name="collection_test" pageWidth="595" pageHeight="842" columnWidth="555" leftMargin="20" rightMargin="20" topMargin="20" bottomMargin="20" uuid="4a22e2a3-efb0-425f-a139-0cf337cfec5e">

<property name="com.jaspersoft.studio.data.defaultdataadapter" value="One Empty Record"/>

<parameter name="issue-status-filter" class="java.util.List"/>

<parameter name="issue-status-filter-clause" class="java.lang.String" isForPrompting="false">

<defaultValueExpression><![CDATA[($P{issue-status-filter} == null || $P{issue-status-filter}.isEmpty()) ? "" : ("and status in ('" + String.join("', '", $P{issue-status-filter}) + "')")]]></defaultValueExpression>

</parameter>

<queryString>

<![CDATA[select 1]]>

</queryString>

<background>

<band splitType="Stretch"/>

</background>

<detail>

<band height="125" splitType="Stretch">

<textField>

<reportElement x="65" y="43" width="235" height="30" uuid="4b00b860-c1fd-4ae3-a254-2828e90ca286"/>

<textFieldExpression><![CDATA["Status Types:" + $P{issue-status-filter-clause}]]></textFieldExpression>

</textField>

</band>

</detail>

</jasperReport>

 

This report works as expected in JasperStudio. Additionally, it in JasperServer works when I pass it two or more parameters in the report request. However, when I try to pass it a single item in the Collection, it throws a ClassCastException. Here is the relevant request:

curl -u superuser:XXXXXX "http://localhost:8080/jasperserver-pro/rest_v2/reports/reports/collection_test.pdf?issue-status-filter=open" -o issue.pdf

Here is the resulting stacktrace

jasperserver_1  | Caused by: java.lang.ClassCastException: java.lang.String cannot be cast to java.util.List
jasperserver_1  |     at collection_test_1510604074207_861928.evaluate(collection_test_1510604074207_861928:137)
jasperserver_1  |     at net.sf.jasperreports.engine.fill.JREvaluator.evaluate(JREvaluator.java:276)
jasperserver_1  |     ... 17 more

 

From what I can see, it appears that Jasper is internally converting the single-element collection to a String when running on the server, and failing when trying to evaulate the expression.

Environment Details:

Jasper Server Pro - 6.3.0

Jasper Studio - 6.3.0

 

I also tested against Jasper Server CE 6.4.0, and got the same results. 

 

 

 

 

 

 

 

 

 

Link to comment
Share on other sites

  • Replies 2
  • Created
  • Last Reply

Top Posters In This Topic

Top Posters In This Topic

  • Solution

I assume you don't have an input control defined for the issue-status-filter parameter in the server report unit.

The recommended solution is to create a multi valued input control for the parameter.  The input control can be either backed by a query (something like select distinct status from ... in your case) or by a static values list (if there's a fixed set of status values).

I would also recommeng using $X{IN,status,issue-status-filter} instead of manually constructing the status in (...) clause with String.join.  For one, $X{IN} protects you from SQL injection.

If for some reason you don't want to create an input control, your only chance is to write the parameter so it works with both single values and collections of values.  Something like this:

    <parameter name="issue-status-filter" class="java.lang.Object" />    <parameter name="issue-status-filter-list" class="java.util.List" isForPrompting="false">        <defaultValueExpression>        ($P{issue-status-filter} == null) ? null            : (($P{issue-status-filter} instanceof Collection)                ? new ArrayList((Collection) $P{issue-status-filter})                : Collections.singletonList($P{issue-status-filter}))        </defaultValueExpression>    </parameter>[/code]

Then you can use issue-status-filter-list for the query.  It's not pretty but I don't see any other way.

Regards,

Lucian

Link to comment
Share on other sites

Thanks Lucian. I came up with a similar ternary operator earlier this morning - I agree it is a little messy, but I was able to get it to work:

 

    <parameter name="issue-status-filter" class="java.lang.Object"/>    <parameter name="issue-status-filter-clause" class="java.lang.String" isForPrompting="false">        <defaultValueExpression><![CDATA[$P{issue-status-filter} == null ? "" :     $P{issue-status-filter} instanceof java.util.Collection ?        ((java.util.Collection<String>)$P{issue-status-filter}).isEmpty() ? "" :             "and status in ('" + String.join("', '", ((java.util.Collection<String>)$P{issue-status-filter})) + "')" :      $P{issue-status-filter} instanceof java.lang.String ?          ((java.lang.String)$P{issue-status-filter}).equals("") ? "" :            "and status in ('" + ((java.lang.String)$P{issue-status-filter}).toString() + "')" : "";]]></defaultValueExpression>    </parameter>    

I haven't looked at creating an input control yet, however - if I get some time I'll see if that helps our use case.

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×
×
  • Create New...