Skip to main content

Mapping the model to the template

The mapping file can contain a number of different mappings. This chapter will explain each mapping available.

Value​

The value mapping is the simplest mapping. It creates a variable to use in the template by defining a name and an expression to calculate it:

<value name="component" eval="dataElement.component.name"/>

Expressions in mapping files are written in OGNL.

ConditionalValue​

A conditionalValue will allow you to define a value which differs based on a set of conditions:

In this example, the value fileName can be overwritten by defining the option 'demo.filename'.

<conditionalValue name="fileName">
<option if="dataElement.getOption('demo.fileName').defined" eval="dataElement.getOption('demo.fileName').value"/>
<defaultOption eval="dataElement.name"/>
</conditionalValue>

A conditionalValue can contain multiple options. The expander will resolve the conditions top-down. Once a condition returns true, the value will be calculated with the eval expression.

Group​

A group mapping will group values into an object. E.g. in this example the values in the group can be accessed in the template as <targetElement.packageName> and <targetElement.name>:

<group name="targetElement" if="field.linkField neq null">
<value name="name" eval="field.linkField.targetElement.name"/>
<value name="packageName" eval="field.linkField.targetElement.packageName"/>
</group>

An if condition can be added to only create the group if that condition is fulfilled.

Let​

The let mapping does not have any direct effect on the template. Rather, it's used to define variables which can be used in other mappings.

See Extending the scope.

List​

The list mapping iterates over a collection and creates a list of mapped objects.

<!--
Get a list containing the names of the Fields in a DataElement.
-->
<list name="fields">
<foreach name="field" in="dataElement.fields"/>
<value name="name" eval="field.name"/>
</list>
For-Each Syntax

The foreach syntax has been introduced in expanders-core:8.1.0

Chaining For-Each

A list mapping can contain multiple <foreach> mappings. The iterations will be nested, but the end result will be a single flat list.

<!--
Obtain every field in a component.
-->
<list name="fields">
<foreach name="dataElement" in="component.dataElements"/>
<foreach name="field" in="dataElement.fields"/>
<value name="dataElementName" eval="dataElement.name"/>
<value name="name" eval="field.name"/>
</list>

Filtering List​

Use the <filter> mapping to select which items to include.

<list name="fields">
<foreach name="field" in="dataElement.fields"/>
<!-- Only take fields where the valueField is defined. -->
<filter name="isValueField" eval="field.valueField neq null"/>
<value name="name" eval="field.name"/>
</list>

Duplicates can be removed by using <distinct>.

<list name="fieldTypes">
<foreach name="field" in="dataElement.fields"/>
<filter name="isValueField" eval="field.valueField neq null"/>
<!-- Remove all items that are duplicates based on the value presented in the eval. -->
<distinct name="distinctTypes" eval="field.valueField.type"/>
<value name="name" eval="field.valueField.type.name"/>
</list>

Sorting​

Lists can be sorted with <sorted>.

<list name="fields">
<foreach name="field" in="dataElement.fields"/>
<value name="name" eval="field.name"/>
<!-- Sort by field name -->
<sorted eval="field.name"/>
</list>

List Composition​

Use <item> to add a single static item to a list.

<list name="fields">
<!-- Always add an id field -->
<item>
<value name="name" eval="'id'"/>
</item>
<foreach name="field" in="dataElement.fields"/>
<value name="name" eval="field.name"/>
</list>

Use <items> to combine lists.

<!--
Create a separate mapping for LinkFields and ValueFields and then combine them.
-->
<list name="fields">
<items>
<foreach name="field" in="dataElement.fields"/>
<filter name="isValueField" eval="field.valueField neq null"/>
<value name="name" eval="field.name"/>
<value name="type" eval="field.valueField.javaType"/>
</items>
<items>
<foreach name="field" in="dataElement.fields"/>
<filter name="isLinkField" eval="field.linkField neq null"/>
<value name="name" eval="field.name"/>
<value name="type" eval="field.linkField.linkFieldType.dataType"/>
</items>
</list>

Assert​

You can check if the model is valid by using asserts:

<assert name="versionField_isValueFieldType" message="Option `isVersion` can only be used in combination with a valueFieldType"
eval="field.valueField neq null"/>

Include​

Lastly, the include mapping will include another mapping file and add it to the mapping.

<include path="SomeCommonMapping.xml"/>
<!-- or -->
<include path="/path/to/SomeCommonMapping.xml"/>

Extended OGNL functionality​

String formatting​

A few attributes have been added to help with java.lang.String formatting.

AttributeExample
.upper,.toUpperdataElement to DATAELEMENT
.lower,.toLowerDataElement to dataelement
.firstToUpperdataElement to DataElement
.firstToLowerDataElement to dataElement
.path,toPathnet.demo.package to net/demo/package
.kebabdataElement to data-element
.camelCasedata_element to dataElement
.snakeCasedataElement to data_element
.pascalCasedataElement to DataElement

String interpolation​

The mapping file supports string interpolation to create Strings in a compact way.

<value name="path" eval="format('${component.name}/${dataElement.name}')"/>

List helpers​

The java.util.List class has been extended to provide the following commonly used operations.

AttributeDescription & Example
.exists(predicate)List contains an element matching the predicate.
dataElement.fields.exists(:[ #this.linkField neq null ])
"The dataElement has linkFields"
.notEmptyList has one or more elements.
component.dataElements.notEmpty
"component has dataElements"
.flattenFlatten a List<List<T>> to List<T>.
application.components.{ dataElements }.flatten
"get a list of dataElements in an application"
.distinct
expanders-core8.5.0+
Filter a List<T> to find all unique values.
dataElement.fields.{? #this.linkField neq null }.{ linkField.targetElement }.distinct
"get a list of dataElements linked to by the linkFields in a dataElement"
.reverse
expanders-core8.5.0+
Reverse the order of a List<T>.
component.dataElements.reverse
"get a list of fields in a dataElement in reverse order"
.first
expanders-core8.5.0+
Return the first element of a List<T> or an throw an exception if the list is empty.
dataElement.fields.first
"get the first field of a dataElement"
.firstOrNull
expanders-core8.5.0+
Return the first element of a List<T> or null if the list is empty.
dataElement.fields.firstOrNull
"get the first field of a dataElement"
.last
expanders-core8.5.0+
Return the last element of a List<T> or an throw an exception if the list is empty.
dataElement.fields.last
"get the last field of a dataElement"
.lastOrNull
expanders-core8.5.0+
Return the last element of a List<T> or null if the list is empty.
dataElement.fields.lastOrNull
"get the last field of a dataElement"

Lambda support​

Ognl lambdas (:[ ... ]) can be used to define Function, Supplier and Predicate from the java.util.function package.

ClassExample
Functionoptional.map(:[ #this.name ])
Supplieroptional.orElseGet(:[ #root.dataElement.name ])
Predicatelist.exists(:[ #this.getOption('option').defined ])