Write Arrays and Nested Records to XML using FreeMarker Templates

This example writes records containing arrays and nested records to XML using FreeMarker templates. It provides a flexible and customizable approach to generating XML output based on data structures and templates.

FreeMarker template follows the MVC pattern to write dynamic content to XML without manually changing the template. By implementing this example you can perform a variety of XML-based transformations, some of which include:

  • Automating report generation process for various financial and invoicing systems
  • Metadata for databases
  • Creating configuration files (e.g. for cloud and on-premise environments of a system)

Here is a step-by-step guide:

 

Input Files

The following files are being used as templates:

header.xml

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<actors>
	<north-america>

 

detail.xml

<actor stage-name="${stageName}" real-name="${realName}">
    <gender>${gender}</gender>
    <city>${city}</city>
    <alternate-address>
        <#if (balance > 150)>
            <street> ${alternateAddress[0].street}</street>
            <city> ${alternateAddress[0].city}</city>
            <province> ${alternateAddress[0].province}</province>
            <zipCode> ${alternateAddress[0].zipCode}</zipCode>
            <home-phone> ${alternateAddress[0].contactNumber.home!}</home-phone>
            <office-phone> ${alternateAddress[0].contactNumber.office!}</office-phone>
        <#else>
            <street> ${alternateAddress[1].street}</street>
            <city> ${alternateAddress[1].city}</city>
            <province> ${alternateAddress[1].province}</province>
            <zipCode>${alternateAddress[1].zipCode}</zipCode>
            <home-phone>${alternateAddress[1].contactNumber.home!}</home-phone>
            <office-phone> ${alternateAddress[1].contactNumber.office!}</office-phone>
        </#if>
    </alternate-address>
</actor>

 

footer.xml

	</north-america>
</actors>

 

Java Code Listing

package com.northconcepts.datapipeline.examples.template;

import com.northconcepts.datapipeline.core.ArrayValue;
import com.northconcepts.datapipeline.core.DataReader;
import com.northconcepts.datapipeline.core.Record;
import com.northconcepts.datapipeline.core.RecordList;
import com.northconcepts.datapipeline.job.Job;
import com.northconcepts.datapipeline.memory.MemoryReader;
import com.northconcepts.datapipeline.template.TemplateWriter;

import java.io.File;
import java.io.FileWriter;

public class WriteArraysAndNestedRecordsToXMLUsingFreeMarkerTemplates {


    private static DataReader reader;

    public static void main(String... args) throws Throwable{
        createRecords();

        TemplateWriter writer = new TemplateWriter(new FileWriter("example/data/output/template-output.xml"));
        writer.setFieldNamesInFirstRow(false);
        writer.getConfiguration().setDirectoryForTemplateLoading(new File("example/data/input/template"));
        writer.setHeaderTemplate("header.xml");
        writer.setFooterTemplate("footer.xml");
        writer.setDetailTemplate("detail.xml");

        Job.run(reader, writer);
    }

    private static void createRecords() {
        Record home = new Record();
        home.getField("home", true).setValue("418-591-6446");

        Record office = new Record();
        office.getField("office", true).setValue("306-612-8887");

        Record address1 = new Record();
        address1.getField("street", true).setValue("1495  Lake City Way");
        address1.getField("city", true).setValue("Burnaby");
        address1.getField("province", true).setValue("British Columbia");
        address1.getField("zipCode", true).setValue("V5A 2Z6");
        address1.getField("contactNumber", true).setValue(home); // nested record

        Record address2 = new Record();
        address2.getField("street", true).setValue("4599  rue des Champs");
        address2.getField("city", true).setValue("Chicoutimi");
        address2.getField("province", true).setValue("Quebec");
        address2.getField("zipCode", true).setValue("G7H 4N3");
        address2.getField("contactNumber", true).setValue(office); // nested record

        ArrayValue arrayValue = new ArrayValue();
        arrayValue.addValue(address1);
        arrayValue.addValue(address2);

        Record record1 = new Record();
        record1.getField("stageName", true).setValue("John Wayne");
        record1.getField("realName", true).setValue("Marion Robert Morrison");
        record1.getField("gender", true).setValue("male");
        record1.getField("city", true).setValue("Winterset");
        record1.getField("balance", true).setValue(156.35);
        record1.getField("alternateAddress", true).setValue(arrayValue);

        Record record2 = new Record();
        record2.getField("stageName", true).setValue("Spiderman");
        record2.getField("realName", true).setValue("Peter Parker");
        record2.getField("gender", true).setValue("male");
        record2.getField("city", true).setValue("New York");
        record2.getField("balance", true).setValue(-0.96);
        record2.getField("alternateAddress", true).setValue(arrayValue.clone());

        reader = new MemoryReader(new RecordList(record1, record2));
    }
}

 

Code Walkthrough

  1. Beginning the execution, createRecords() method creates sample data records with nested fields and arrays.
  2. A MemoryReader is used to initialize the reader object with the prepared record list.
  3. Then, a TemplateWriter is configured with the output file path, template directory, and template files header.xml, footer.xmldetail.xml sections.
  4. Finally, Job.run() method is invoked with reader and writer objects to store the output to template-output.xml file.

 

Output File

The following XML is populated in the template-output.xml file:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<actors>
	<north-america>
		<actor stage-name="John Wayne" real-name="Marion Robert Morrison">
			<gender>male</gender>
			<city>Winterset</city>
			<alternate-address>
				<street>1495 Lake City Way</street>
				<city>Burnaby</city>
				<province>British Columbia</province>
				<zipCode>V5A 2Z6</zipCode>
				<home-phone>418-591-6446</home-phone>
				<office-phone></office-phone>
			</alternate-address>
		</actor>
		<actor stage-name="Spiderman" real-name="Peter Parker">
			<gender>male</gender>
			<city>New York</city>
			<alternate-address>
				<street>4599 rue des Champs</street>
				<city>Chicoutimi</city>
				<province>Quebec</province>
				<zipCode>G7H 4N3</zipCode>
				<home-phone></home-phone>
				<office-phone>306-612-8887</office-phone>
			</alternate-address>
		</actor>
	</north-america>
</actors>
Mobile Analytics