jasperreports icon indicating copy to clipboard operation
jasperreports copied to clipboard

JsonDataSource not working good with locales

Open abnud1 opened this issue 5 years ago • 1 comments

I have a report which has a subreport, the subreport data source is a JsonDataSource from a ByteArrayInputStream containing json bytes passed as parameter to original report.

One of the fields of the json is a double number, I when I try printing report to pdf using jasperreports java lib, I get this exception:

net.sf.jasperreports.engine.JRRuntimeException: net.sf.jasperreports.engine.JRException: Unable to get value for JSON field "GrdDividerArea1" of class java.lang.Double. at net.sf.jasperreports.engine.fill.JRFillSubreport.prepare(JRFillSubreport.java:962) at net.sf.jasperreports.engine.fill.JRFillElementContainer.prepareElements(JRFillElementContainer.java:542) at net.sf.jasperreports.engine.fill.JRFillBand.fill(JRFillBand.java:453) at net.sf.jasperreports.engine.fill.JRFillBand.fill(JRFillBand.java:428) at net.sf.jasperreports.engine.fill.JRVerticalFiller.fillColumnBand(JRVerticalFiller.java:2599) at net.sf.jasperreports.engine.fill.JRVerticalFiller.fillDetail(JRVerticalFiller.java:823) at net.sf.jasperreports.engine.fill.JRVerticalFiller.fillReportStart(JRVerticalFiller.java:264) at net.sf.jasperreports.engine.fill.JRVerticalFiller.fillReport(JRVerticalFiller.java:110) at net.sf.jasperreports.engine.fill.JRBaseFiller.fill(JRBaseFiller.java:615) at net.sf.jasperreports.engine.fill.BaseReportFiller.fill(BaseReportFiller.java:433) at net.sf.jasperreports.engine.fill.JRFiller.fill(JRFiller.java:162) at net.sf.jasperreports.engine.fill.JRFiller.fill(JRFiller.java:145) at net.sf.jasperreports.engine.JasperFillManager.fill(JasperFillManager.java:758) at net.sf.jasperreports.engine.JasperFillManager.fillReport(JasperFillManager.java:1074) at com.i4all.controllers.ReportController.report(ReportController.kt:127) at com.i4all.controllers.ReportController$Module.postReport$reportName$taskId$ctx(com.i4all.controllers.ReportController$Module.java) at io.jooby.Route$Before.lambda$then$e67b40fd$1(Route.java:133) at io.jooby.Route$Handler.lambda$then$3a6f0e7d$1(Route.java:268) at io.jooby.internal.handler.SendDirect.apply(SendDirect.java:22) at io.jooby.internal.handler.WorkerHandler.lambda$apply$0(WorkerHandler.java:23) at org.jboss.threads.ContextClassLoaderSavingRunnable.run(ContextClassLoaderSavingRunnable.java:35) at org.jboss.threads.EnhancedQueueExecutor.safeRun(EnhancedQueueExecutor.java:2019) at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask(EnhancedQueueExecutor.java:1558) at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1449) at java.base/java.lang.Thread.run(Thread.java:834) Caused by: net.sf.jasperreports.engine.JRException: Unable to get value for JSON field "GrdDividerArea1" of class java.lang.Double. at net.sf.jasperreports.engine.data.JsonDataSource.getFieldValue(JsonDataSource.java:277) at net.sf.jasperreports.engine.fill.JRFillDataset.setOldValues(JRFillDataset.java:1501) at net.sf.jasperreports.engine.fill.JRFillDataset.next(JRFillDataset.java:1402) at net.sf.jasperreports.engine.fill.JRFillDataset.next(JRFillDataset.java:1378) at net.sf.jasperreports.engine.fill.JRBaseFiller.next(JRBaseFiller.java:1194) at net.sf.jasperreports.engine.fill.JRVerticalFiller.fillReport(JRVerticalFiller.java:108) at net.sf.jasperreports.engine.fill.JRBaseFiller.fill(JRBaseFiller.java:615) at net.sf.jasperreports.engine.fill.BaseReportFiller.fill(BaseReportFiller.java:433) at net.sf.jasperreports.engine.fill.JRFillSubreport.fillSubreport(JRFillSubreport.java:824) at net.sf.jasperreports.engine.fill.JRSubreportRunnable.run(JRSubreportRunnable.java:61) at net.sf.jasperreports.engine.fill.AbstractThreadSubreportRunner.run(AbstractThreadSubreportRunner.java:221) at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) ... 1 more Caused by: org.apache.commons.beanutils.ConversionException: Unparseable number: "-8.747558720707385E7" at org.apache.commons.beanutils.locale.BaseLocaleConverter.convert(BaseLocaleConverter.java:245) at org.apache.commons.beanutils.locale.LocaleConvertUtilsBean.convert(LocaleConvertUtilsBean.java:286) at net.sf.jasperreports.engine.data.JRAbstractTextDataSource.convertStringValue(JRAbstractTextDataSource.java:78) at net.sf.jasperreports.engine.data.JsonDataSource.getFieldValue(JsonDataSource.java:261) ... 13 more Caused by: java.text.ParseException: Unparseable number: "-8.747558720707385E7" at java.base/java.text.NumberFormat.parse(NumberFormat.java:431) at org.apache.commons.beanutils.locale.converters.DecimalLocaleConverter.parse(DecimalLocaleConverter.java:253) at org.apache.commons.beanutils.locale.converters.DoubleLocaleConverter.parse(DoubleLocaleConverter.java:217) at org.apache.commons.beanutils.locale.BaseLocaleConverter.convert(BaseLocaleConverter.java:236) ... 16 more

Now looking at this code:

else if (Number.class.isAssignableFrom(valueClass)) {
						//FIXME if the json node is a number, avoid converting to string and parsing back the value
							value = convertStringValue(selectedObject.asText(), valueClass);
							
					}

The problem we have here is that jackson's asText will convert the double number to string using english locale, I'm afraid that the default locale of the JVM is arabic, I've verified this by putting a breakpoint at apache commons-beanutils code here:

/**
     * Convert the specified locale-sensitive input object into an output
     * object of the specified type.
     *
     * @param value The input object to be converted
     * @param pattern The pattern is used for the convertion
     * @return The converted value
     *
     * @throws org.apache.commons.beanutils.ConversionException if conversion
     * cannot be performed successfully
     * @throws ParseException if an error occurs parsing a String to a Number
     */
    @Override
    protected Object parse(final Object value, final String pattern) throws ParseException {

        if (value instanceof Number) {
            return value;
        }

        // Note that despite the ambiguous "getInstance" name, and despite the
        // fact that objects returned from this method have the same toString
        // representation, each call to getInstance actually returns a new
        // object.
        final DecimalFormat formatter = (DecimalFormat) DecimalFormat.getInstance(locale); // breakpoint is set on this line and I confirmed that locale is arabic

        // if some constructors default pattern to null, it makes only sense
        // to handle null pattern gracefully
        if (pattern != null) {
            if (locPattern) {
                formatter.applyLocalizedPattern(pattern);
            } else {
                formatter.applyPattern(pattern);
            }
        } else {
            log.debug("No pattern provided, using default.");
        }

        return formatter.parse((String) value);
    }

at org.apache.commons.beanutils.locale.converters.DecimalLocaleConverter.java

This is what causes the exception, my suggestion is to either fix the //FIXME comment or use english locale for parsing such numbers since the result is a double so locale doesn't matter really here.

Oh I'm using arabic locale since all reports are arabic :).

abnud1 avatar Jun 28 '20 17:06 abnud1

Hi, Could you post a JRXML and data sample demonstrating this problem? Thank you, Sanda

shertage avatar Aug 03 '20 14:08 shertage