Write Arrays and Nested Records to XML using FreeMarker Templates
Updated: Jul 1, 2023
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
- Beginning the execution,
createRecords()
method creates sample data records with nested fields and arrays. - A
MemoryReader
is used to initialize thereader
object with the prepared record list. - Then, a
TemplateWriter
is configured with the output file path, template directory, and template filesheader.xml
,footer.xml
,detail.xml
sections. - Finally,
Job.run()
method is invoked withreader
andwriter
objects to store the output totemplate-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>