pbuchheit
pbuchheit

Reputation: 1637

How to Use Freemarker Templates to Output JSON

I have an application that needs to process some data and output it as a JSON file. Rather than process everything manually, I thought I could use a templating library to structure the output. Freemarker seemed to offer what I was looking for, but I have run into a problem when dealing with lists.

The classes for my data model look like this:

public class Order {    
    public String orderNumber;
    public Date processed;
    public Boolean release; 
    public List<OrderLine> lines;       
}

public class OrderLine {
    public String unit;
    public Integer quantity;
    public String itemNumber;
}

The JSON output should look like:

{"OrderNo": "1055249", "OrderDate": "202103251951", "Release": true, "OrderLines": [
{"UnitOfMeasure": "EA", "Quantity": "1", "ItemNo": "764724473433"},
{"UnitOfMeasure": "EA", "Quantity": "1", "ItemNo": "764724810979"}
]}

Notice the list of OrderLines. I can put together a template for the other properties easily enough, but I have no idea how to handle a repeated element like an OrderLine. The built in list directive prints out each element but I need a comma separated list of values.

Does anyone know of a way to do this in freemarker without having to create a custom directive? If not, is there another templating library better suited to what I'm trying to do?

Upvotes: 0

Views: 13894

Answers (1)

pcsutar
pcsutar

Reputation: 1829

You can use inbuilt loop vars to generate comma separated list of order lines. Please refer below example:

Order.ftl

<#import "OrderLine.ftl" as OrderLine>
{
    "OrderNo": "${order.orderNumber}",
    "OrderDate": "${order.processed?datetime}",
    "Release": ${order.release?c},
    "OrderLines": [
        <@OrderLine.OrderLine order.lines/>
    ]
}

OrderLine.ftl

<#macro OrderLine lines>
<#list lines as line>
        {
            "unit": "${line.unit}",
            "quantity": "${line.quantity}",
            "itemNumber": "${line.itemNumber}"
        }<#if line?is_last == false>,</#if>
</#list>
</#macro>

In above ftl I have used ?is_last loop var to add comma (,) between two order line objects in the list.

Also, <#if line?is_last == false>,</#if> can be replaced with <#sep>,</#sep>. check sep directive

FreemarkerService.java

import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import freemarker.template.Configuration;
import freemarker.template.Template;

public class FreemarkerService {

    private static final Configuration CONFIGURATION = new Configuration(Configuration.VERSION_2_3_0);

    public static void main(String args[]) throws Exception {
        OrderLine orderLine   = new OrderLine("EA", 1, "764724473433");
        OrderLine orderLine2  = new OrderLine("EA", 1, "764724810979");
        List<OrderLine> lines = new ArrayList<>();
        lines.add(orderLine);
        lines.add(orderLine2);

        Order order = new Order("1055249", new Date(), true, lines);

        Map<String, Object> input = new HashMap<>();
        input.put("order", order);

        String json = generateJsonByTemplate("Order.ftl", input);

        System.out.println(json);
    }

    public static String generateJsonByTemplate(String templateName, Map<String, Object> input) throws Exception
    {
        String sourceCode = null;

        try
        {
            Template template   = CONFIGURATION.getTemplate(templateName);
            StringWriter writer = new StringWriter();

            template.process(input, writer);
            sourceCode = writer.toString();
        }
        catch (Exception exception)
        {
            throw new Exception("Processing failed for template '" + templateName  + "' with error: " + exception.getMessage(), exception);
        }

        return sourceCode;
    }
}

Output:

{
    "OrderNo": "1055249",
    "OrderDate": "Aug 7, 2021 11:43:34 AM",
    "Release": true,
    "OrderLines": [
        {
            "unit": "EA",
            "quantity": "1",
            "itemNumber": "764724473433"
        },
        {
            "unit": "EA",
            "quantity": "1",
            "itemNumber": "764724810979"
        }
    ]
}

Upvotes: 1

Related Questions