JasperReports - JSON Data Report Shows Null Values when run in Java

Allawy M :

I am testing JasperReports with JSON data and running into a problem where null values are being displayed when generated from within a Java application. Here is what I have done so far:

In Studio, I created a report that uses the JSON File data provider using a file containing the following JSON:

{
    "employees": [
        {
            "fullname":"John Stark",
            "employeeid":"29388282773",
            "phone":"415-293-2928"
        },
        {
            "fullname":"Mike Goodmann",
            "employeeid":"2938828282",
            "phone":"415-293-2726"
        },
        {
            "fullname":"David Simpson",
            "employeeid":"2938822837",
            "phone":"415-293-9826"
        },
        {
            "fullname":"Chris Humpty",
            "employeeid":"2938275452",
            "phone":"415-293-1122"
        }
    ]
}

Here is the resulting jrxml file:

<?xml version="1.0" encoding="UTF-8"?>
<!-- Created with Jaspersoft Studio version 7.1.0.final using JasperReports Library version 6.4.3  -->
<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="testreport" pageWidth="792" pageHeight="612" orientation="Landscape" columnWidth="752" leftMargin="20" rightMargin="20" topMargin="20" bottomMargin="20" >
    <property name="com.jaspersoft.studio.data.defaultdataadapter" value="testdata"/>
    <queryString language="json">
        <![CDATA[employees]]>
    </queryString>
    <field name="fullname" class="java.lang.String">
        <property name="net.sf.jasperreports.json.field.expression" value="fullname"/>
        <fieldDescription><![CDATA[fullname]]></fieldDescription>
    </field>
    <field name="employeeid" class="java.lang.Long">
        <property name="net.sf.jasperreports.json.field.expression" value="employeeid"/>
        <fieldDescription><![CDATA[employeeid]]></fieldDescription>
    </field>
    <field name="phone" class="java.lang.String">
        <property name="net.sf.jasperreports.json.field.expression" value="phone"/>
        <fieldDescription><![CDATA[phone]]></fieldDescription>
    </field>
    <title>
        <band height="79" splitType="Stretch">
            <staticText>
                <reportElement x="240" y="24" width="280" height="30" />
                <text><![CDATA[Employee List]]></text>
            </staticText>
        </band>
    </title>
    <columnHeader>
        <band height="65" splitType="Stretch">
            <staticText>
                <reportElement x="30" y="30" width="180" height="30" />
                <text><![CDATA[Full Name]]></text>
            </staticText>
            <staticText>
                <reportElement x="240" y="30" width="160" height="30" />
                <text><![CDATA[Employee Id]]></text>
            </staticText>
            <staticText>
                <reportElement x="430" y="30" width="180" height="30" />
                <text><![CDATA[Phone Number]]></text>
            </staticText>
            <staticText>
                <reportElement x="299" y="0" width="100" height="30" />
                <text><![CDATA[employeeid]]></text>
            </staticText>
            <staticText>
                <reportElement x="468" y="0" width="100" height="30" />
                <text><![CDATA[phone]]></text>
            </staticText>
        </band>
    </columnHeader>
    <detail>
        <band height="24" splitType="Stretch">
            <textField>
                <reportElement x="30" y="0" width="180" height="20" />
                <textFieldExpression><![CDATA[$F{fullname}]]></textFieldExpression>
            </textField>
            <textField>
                <reportElement x="241" y="0" width="159" height="20" />
                <textFieldExpression><![CDATA[$F{employeeid}]]></textFieldExpression>
            </textField>
            <textField>
                <reportElement x="430" y="0" width="180" height="20" />
                <textFieldExpression><![CDATA[$F{phone}]]></textFieldExpression>
            </textField>
        </band>
    </detail>
</jasperReport>

Then wrote a simple console app to generate the report in PDF but I get one row with NULL values. You will notice that I embedded the JSON into the class to simplify the test code until I get it to run correctly, and that I am indeed passing the JSON data into the "JasperFillManager.fillReport()" call. Here is my Java code:

public class ReportTester {
    String jsonData = "{\n" +
            "    \"employees\": [\n" +
            "        {\n" +
            "            \"fullname\":\"John Stark\",\n" +
            "            \"employeeid\":\"29388282773\",\n" +
            "            \"phone\":\"415-293-2928\"\n" +
            "        },\n" +
            "        {\n" +
            "            \"fullname\":\"Mike Goodmann\",\n" +
            "            \"employeeid\":\"2938828282\",\n" +
            "            \"phone\":\"415-293-2726\"\n" +
            "        },\n" +
            "        {\n" +
            "            \"fullname\":\"David Simpson\",\n" +
            "            \"employeeid\":\"2938822837\",\n" +
            "            \"phone\":\"415-293-9826\"\n" +
            "        },\n" +
            "        {\n" +
            "            \"fullname\":\"Chris Humpty\",\n" +
            "            \"employeeid\":\"2938275452\",\n" +
            "            \"phone\":\"415-293-1122\"\n" +
            "        }\n" +
            "    ]\n" +
            "}";
    String reportFile = "/testreport.jrxml";
    String outputPdf = "testreport.pdf";
    JasperReport jasperReport;

    public void printme() {
        try {
            InputStream employeeReportStream = getClass().getResourceAsStream(reportFile);
            jasperReport = JasperCompileManager.compileReport(employeeReportStream);

            ByteArrayInputStream jsonDataStream = new ByteArrayInputStream(jsonData.getBytes());
            JsonDataSource ds = new JsonDataSource(jsonDataStream);
            JasperPrint jasperPrint = JasperFillManager.fillReport(jasperReport, new HashMap<String, Object>(), ds);

            JRPdfExporter exporter = new JRPdfExporter();
            exporter.setExporterInput(new SimpleExporterInput(jasperPrint));
            exporter.setExporterOutput(new SimpleOutputStreamExporterOutput(outputPdf));
            SimplePdfReportConfiguration reportConfig = new SimplePdfReportConfiguration();
            reportConfig.setSizePageToContent(true);
            reportConfig.setForceLineBreakPolicy(false);
            exporter.setConfiguration(reportConfig);

            exporter.exportReport();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Can anyone help point out what is wrong with this and why the report does not generate with data?

Alex K :

What is wrong

You did not select data right. It means that JR engine prepared wrong input data for the report.

How to fix

All you need is to pass expression to the JsonDataSource instance for selecting right data.

Example:

JasperReport jasperReport;
try {
    try (InputStream inputStream = getClass().getResourceAsStream(reportTemplate)) {
        jasperReport = JasperCompileManager.compileReport(inputStream);
    }

    File outputFile = new File(outputFileName);
    SimplePdfExporterConfiguration configuration = new SimplePdfExporterConfiguration();
    ByteArrayInputStream jsonDataStream = new ByteArrayInputStream(jsonData.getBytes());
    JsonDataSource ds = new JsonDataSource(jsonDataStream, "employees");
    JasperPrint jasperPrint = JasperFillManager.fillReport(jasperReport, Maps.newHashMap(), ds);

    try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
         OutputStream fileOutputStream = new FileOutputStream(outputFile)) {
        JRPdfExporter exporter = new JRPdfExporter();

        exporter.setExporterInput(new SimpleExporterInput(jasperPrint));
        exporter.setExporterOutput(new SimpleOutputStreamExporterOutput(byteArrayOutputStream));
        exporter.setConfiguration(configuration);
        exporter.exportReport();
        byteArrayOutputStream.writeTo(fileOutputStream);
    }
} catch (IOException | JRException e) {
    throw new RuntimeException("Failed to build report", e);
}

All magic is here:

JsonDataSource ds = new JsonDataSource(jsonDataStream, "employees");

With help of expression employees we are selecting all nodes. You are doing the same at template or at data adapter at JSS.

We can view the source code of JsonDataSource class. We need moveFirst method.

@Override
public void moveFirst() throws JRException {
    if (jsonTree == null || jsonTree.isMissingNode()) {
        throw 
            new JRException(
                EXCEPTION_MESSAGE_KEY_NO_DATA,
                (Object[])null);
    }

    currentJsonNode = null;
    JsonNode result = getJsonData(jsonTree, selectExpression);
    if (result != null && result.isObject()) {
        final List<JsonNode> list = new ArrayList<JsonNode>();
        list.add(result);
        jsonNodesIterator = new Iterator<JsonNode>() {
            private int count = -1;
            @Override
            public void remove() {
                list.remove(count);
            }

            @Override
            public JsonNode next() {
                count ++;
                return list.get(count);
            }

            @Override
            public boolean hasNext() {
                return count < list.size()-1;
            }
        };
    } else if (result != null && result.isArray()) {
        jsonNodesIterator = result.elements();
    }
}

In case selecting data with employees expression we have list of "employee" entities having fullname, employeeid and phone attributes.

With help of debugging tool we can view the data at this line of code: JsonNode result = getJsonData(jsonTree, selectExpression);

The data of JsonNode result

In case using your code: ByteArrayInputStream jsonDataStream = new ByteArrayInputStream(jsonData.getBytes()); the data will be:

The JsonNode result without using exression

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=217248&siteId=1