Template

Value resolver – Abstract

Purpose: This interprets a statically defined Template as a parameter, which can include the data of the print XML for the input value via XPath (1.0), and returns the result as a string.

See also: Parse XML

images/download/attachments/169634416/image-2025-3-5_13-35-20-version-1-modificationdate-1741178119761-api-v2.png


IMPORTANT◄ The Template resolver is not available in client context (Client workflow, Client workflows).


The Template resolver interprets a static Template definition configured as the only parameter of the resolver, which may include references to data of the Print XML of the input value and returns an output string as a result:

  • Any serializable object can be used as an input value, either as an individual instance or a list of objects.

  • A list provided as an input value may include objects of various types.

  • Object data appears within the printObjects element of the internally generated print XML document (see example below), which can be processed by XPath (1.0) expressions from within the template.

  • The printObjects element appears in the print XML as a direct child of the root element (<core:Print>) after a set of elements reflecting properties of the current session:

    • The <base:User> element reflects the status of the user account (Users object identified as User of session) at the start time of the current session.
      ►NOTE◄ When a Guest users is logged in, a Guest users element (<base:GuestUser>) is inserted instead.

    • The <base:CompanyAccount> reflects the status of the company account (Company accounts object idenfiied as Company of session) at the start time of the current session.

    • The <locale> element contains the internal key value for the Current locale as (inner) text.

    • The <storage> element contains all variables defined in the runtime context and their values as a list of elements with the following structure: <entry><key>...</key><value>...</value></entry>, where the <key> element defines the variable name as a String and the <value> element holds the value of the variable. An XPath expression like /core:Print/storage/entry[key="test"]/value therefore referes to the value of variable test.
      NOTE◄ Unlike the other elements the <storage> element does not appear in a print XML document, which is generated interactively via ribbon command Show print XML (see following sample). However, in that case variable do not exist anyway.

  • The return value of the resolver is always of the text (string) type, unless the interpretation of the template expression combines with the object data cannot be accomplished, so 'no value' (null) is returned. However, a real error that could be 'handled' with a Try/Catch actor, for example, is not triggered.

Sample content from a print XML document:

For object types featured in overviews or input forms, the export ribbon features a Show print XML command, which produces output like the following (based on the current selection).

The output presented by the neighbouring ribbon command, Show XML, largely matches the data of the same object when featured in the printObjects element of a print XML document.

The following example illustrates the structure of a print XML document base created for a single business object of the 'Shipment' type as a printObject:

Print XML for a shipment
<?xml version="1.0" encoding="UTF-8"?>
<core:Print xmlns:base="SCM.BASE" ... >
<base:User ...>
...
</base:User>
<base:CompanyAccount ...>
...
</base:CompanyAccount>
<locale>de</locale>
<printObjects>
<shp:Shipment id="305" ...>
<attributes>
<shp:ShipmentNumericValue id="63"...>
<value numericValueType="GROSS_WEIGHT" value="3.2" unit="kg"/>
</shp:ShipmentNumericValue>
...
<shp:ShipmentCompanyAddress ...>
<value companyType="CNR" reference="354234653465">
<address salutation="MR" name1="John" name2="Smith"
accNumber="1234567890" accMatchCode="JSmith"
street1="Dusseldorf Blvd." streetNo="15"
countryCode="GB" zipcode="RG6" city="Reading"
stateProvince="England" stateCode="ENG">
<attributes>
<base:AddressCommunicationInfo communicationType="EMAIL" communicationContext="Sales" ...>
<communicationValue>sales@lobster.com</communicationValue>
</base:AddressCommunicationInfo>
...
</attributes>
</address>
</value>
</shp:ShipmentCompanyAddress>
...
<shp:ShipmentDate ...>
<value dateType="DELIVERY_TO">
<date start="1454454000000" timeZone="Europe/Berlin"/>
</value>
</shp:ShipmentDate>
<shp:ShipmentDate ...>
<value dateType="DELIVERY_FIXED">
<date start="1455058800000" end="1455145199999" timeZone="Europe/Berlin"/>
</value>
</shp:ShipmentDate>
...
</attributes>
<lineItems>
<lineItem numberOfPackages="3" typeOfPackaging="BAG" ...>
...
</lineItem>
<lineItem numberOfPackages="1" typeOfPackaging="BOX" ...>
...
</lineItem>
  ...
</lineItems>
</shp:Shipment>
</printObjects>
</core:Print>

Examples for XPath expressions retrieving content from the above print XML document:

Expression

Return value

Description

Since all of the following examples share the same base path (shipment header), this part of the expression is only provided in full on the first occasion. Later examples use relative path notation with respect to the shipment header.

/core:Print/printObjects/shp:Shipment

(complete shipment)

referred to as context ('.') in the following examples

./@id

305

The id property (id) of the shipment is an attribute (addressed with @ as prefix) in the shipment header element (<shp:Shipment>).

./attributes/shp:ShipmentCompanyAddress/value[@companyType='CNR']/@reference

354234653465

A company reference ( reference ) attribute is retrieved from the value element of a company address attribute (shp:ShipmentCompanyAddress) for Company type (companyType) 'Consignor ('CNR').

./attributes/shp:ShipmentDate/value[@dateType='DELIVERY_FIXED']/date/@start

1455058800000

The From-value (start attribute) of the date element value (value) of a date attribute (shp:ShipmentDate) of the Date types (dateType) 'Delivery fixed'('DELIVERY_FIXED') is returned as a long value (indicating UTC milliseconds). In practice, date/time information is often formatted as a readable text value using the date() function (see below).

./attributes/shp:ShipmentNumericValue/value[@numericValueType='GROSS_WEIGHT']/@value

3.2

A plain numeric value (value attribute) is retrieved from a Numeric types (numericValueType) 'Gross weigth' ('GROSS_WEIGHT').

Including XPath expressions in a Template:

XPath expressions and the functions described below must be enclosed in curly brackets '{ ... }', since strings without this marking are directly output as literals.

Additionally, function names must be prefixed by a dollar sign '$' (inside the brackets).

Example: A template to generate output in HTML format:

Example template text
The following shipment was delivered:<br>
<br>
<table>
<tr><td>ShipmentID</td><td>{/core:Print/printObjects/shp:Shipment/@id}</td></tr>
<tr><td>Delivery date fix</td><td>{$date("dd.MM.yyyy HH:mm:ss",{/core:Print/printObjects/shp:Shipment/attributes/shp:ShipmentDate/value[@dateType='DELIVERY_FIXED']/date/@start})}</td></tr>
</table>
<br>
Line items
<br>
<table>
{$foreach({/core:Print/printObjects/shp:Shipment/lineItems/lineItem})
<tr><td>Packages: {@numberOfPackages} of type:{$r("typeOfPackaging",{@typeOfPackaging})}</td></tr>
}
</table>

HTML output with data from a specific shipment:

HTML source code

Representation in a browser document

Output
The following shipment was delivered:<br>
<br>
<table>
<tr><td>ShipmentID</td><td>305</td></tr>
<tr><td>Delivery date fix</td><td>04.02.2016 09:39:00</td></tr>
</table>
<br>
Line items
<br>
<table>
<tr><td>Packages: 3 of type:Bag</td></tr>
<tr><td>Packages: 1 of type:Box</td></tr>
</table>
The following shipment was delivered:
 
ShipmentID
305
 
Delivery date fix
04.02.2016 09:39:00
 
Line items
Packages: 3 of type:Bag
Packages: 1 of type:Box

Function available for processing data in 'Template' expressions

images/s/-95e2zf/9012/8yg2g7/_/images/icons/emoticons/warning.svg IMPORTANTimages/s/-95e2zf/9012/8yg2g7/_/images/icons/emoticons/warning.svg

In the following examples, the expressions shown have been reproduced with line breaks for the sake of clearer presentation, some of which must not occur in this way within a configuration. Copying and pasting such expressions into a specific configuration is therefore not recommended, since errors or unexpected results may occur.


Function

Syntax/Details

Example

date()

Date formatting

{$date(datePattern,dateValue[;Timezone])}

The datePattern uses the syntax for the java class SimpleDateFormat, as specified in detail at the following link:
https://docs.oracle.com/javase/8/docs/api/java/text/SimpleDateFormat.html

Pattern may be enclosed in total by double quotes (""). This enables the use of certain characters such as ( ) or , as part of the pattern, which otherwise will provoke parsing issues.

Literal strings within a pattern can be 'commented out' by single quotes. This may be necessary because plain letters are either representing date components (see mapping below) or not accepted at all.

Example:

{$date("h'h 'mm'm'",{timestamp})} creates output like: "7h 05m"

►IMPORTANT◄ Notation rules applicable for datePatterns explained in this context, are not identical with mappings for date functions in other contexts. Since the characters and their mappings do overlap in part, it is especially important to understand and respect the specifics of mapping and rules precisely and per context when designing patterns.

The following table is provided as quick reference and does not feature full details:

Component

Description

Output values

G

Era (BC/AD)

AD, BC

yy, y or YY, Y

Year

21, 2021

M, MM, MMM, MMMM
L, LL, LLL, LLLL

Month (digits or text)

1-12, 01-12, Jan-Dec, January-December

D, DDD

Day of the year (digits)

1-366 or 001-366

d, dd

Day of the month (digits)

1-31, 01-31

w, ww

Calendar week (digits)

1-53, 01-53

W

Week of the month (digits)

1-5

u, E

Day of the week (digit or text)

1-7, Mon-Sun, Monday-Sunday

F

Day of the week in month (digit)

3 (as in: "3rd Monday in May")

H, HH
k, kk
h, hh (a)
K, KK (a)

Hour of the day

0-23, 00-23
1-24, 01-24
1-12, 01-12 (AM/PM)
0-11, 00-11 (AM/PM)

m, mm

Minute in the hour

0-59, 00-59

s, ss

Second in the minute

0-59, 00-59

S, SSS

Milliseconds in the second

0-999, 000-999

z, Z, X

Time zone (var.)

Europe/Berlin, -0200, -02

Example: Indicate value of date attribute "DELIVERY_FIXED" in default system time zone:

{$date("dd.MM.yyyy HH:mm:ss (z)",{/core:Print/printObjects/shp:Shipment/attributes/shp:ShipmentDate/value[@dateType='DELIVERY_FIXED']/date/@start})}

Result: "04.02.2020 09:39:00 (Europe/Berlin)" ... provided default system time zone is "Europe/Berlin" date attribute exists.

►NOTE◄ If the date attribute addressed by XPath is not found in the print XML of the shipment, the expression fails and the resolver returns 'no value' (null). Using the with() function (see following example), may help to handle this case in an elegant way.


Example: Indicate value of the date attribute "DELIVERY_FIXED" in the time zone stored in the attribute:

{$with(/core:Print/printObjects/shp:Shipment/attributes/shp:ShipmentDate/value[@dateType='DELIVERY_FIXED']/date){$date("dd.MM.yyyy HH:mm:ss"),{@start};{@timeZone})}
}

  • To access both fields 'From' (start) and 'Timezone' (timeZone) within the same date attribute (DELIVERY_FIXED), the date() function is executed here within a with() function (see below), which defines a common point of reference (<date> element of the date attribute) within the print XML as a context for both date() function calls.

Result: "04.02.2020 07:39:00 (UTC)" ... provided the date attribute refers to time zone 'UTC'.

►NOTE◄

  • If the date attribute provides no indication for the time zone, the resolver will return no result overall. This will also occur if the expression refers to one of the date properties 'from' (start) or 'to' (end) when there is no value for that property. Since this is not uncommon, at least if business objects are created by import, such cases should be handled with logical functions like if() and cmp() as demonstrated in later examples of this section.

  • If the date attribute is completely non-existent (in the print XML) no special handling is necessary, as in this case the with() function will not resolve any context and therefore skip the interpretation of the expression including the date() function calls.


Example: Indicate value of date attribute "DELIVERY_FIXED" in the default time zone of User of session:

{$date("dd.MM.yyyy HH:mm:ss (z)",
{/core:Print/printObjects/shp:Shipment/attributes/shp:ShipmentDate/value[@dateType='DELIVERY_FIXED']/date/@start};{/core:Print/base:User/@defaultTimeZone}
)}

  • The print XML contains the user and company account of the session (as explained above). Therefore, the property 'Default time zone' (defaultTimeZone) can be looked up by an XPath expression in the respective child element.

Result: "03.02.2020 21:39:00 (Pacific/Honolulu)" ... if the default time zone for the current user account is "Pacific/Honolulu".

►NOTE◄ If no default time zone is specified in the account of the current user, the resolver returns 'no value' overall. This case can be handled with functions if() and cmp(),, as required. A solution might look as follow:

{$date("dd.MM.yyyy HH:mm:ss (z)",
{/core:Print/printObjects/shp:Shipment/attributes/shp:ShipmentDate/value[@dateType='DELIVERY_FIXED']/date/@start};{$if ({$cmp({/core:Print/base:User/@defaultTimeZone},!=,)}){then {/core:Print/base:User/@defaultTimeZone}}{else UTC}}
)}

  • Time zone 'UTC' is provided as a fallback here, in case no indication for default time zone is found.

fn()

Format number

{$fn(Number[precision=2[,integerDigits=1[,thousandsSeparator=false]]])}

This function creates a formatted String from a numerical value that must be provided or a String that can be interpreted as a numerical value.

The following parameters can be optionally specified to control formatting details:

Parameter

Default value

Effect

precision

2

Number of decimal places output (incl. rounding)

integerDigits

1

Minimum number of integer digits

thousandsSeparator

false

Group integer digits into groups of three with separators?

NOTE◄ The localisations applicable to the Current locale for the language management entries lobsterui/decimalSeparator and lobsterui/thousandsSeparator are used as decimal and thousands separators.

Example: A variable mtom in the data context contains a ‘Unit number’ (UnitNumber), which is to be output with a reference to the unit and exactly 3 decimal places.

{$with({//storage/entry[key='mtom']/value})
max. take-off mass [{@unit}]: {$fn({@value},3)}
}

Result: (Sample data) max. take-off mass [t]: 10,423

NOTE The unit can only be labelled here via the ‘alias’ (see Units), which the XML structure for the UnitNumber contains directly. In contrast, the ‘name’ (name) of the associated dynamic enumeration value (e.g. TON) or the applicable localisation (e.g. ‘tonne’) is used in the string image of a UnitNumber to map the unit. In the context of the Template value resolver, there is no direct way to obtain this localisation based on the ‘Alias’ String.


Example: All IDs and names of the attributes that belong to the ‘address’ (address) of the logged-in user should be listed. The IDs should be mapped as an exact six-digit integer (with ‘leading zeros’ if necessary) and thousands separators.

{$foreach({/core:Print/base:User/address/attributes/*})
#{$fn({@id},0,6,true)} - {name()}}

Result: (Sample data)

#016.153 - base:AddressCommunicationInfo
#016.154 - base:AddressCommunicationInfo
#010.051 - base:AddressContact
#010.052 - base:AddressContact
#011.601 - base:AddressLinkedEntity
#016.251 - base:AddressFlag

r()
(resource)

Localization


{$r(ResourceBundle,ResourceName)}

This function resolves a localization text from Localization for a given combination of Resource Bundle and Resource Name.

The optional prepareValue parameter (default value: true) can be used to switch off (false) escape substitutions (e.g. for ZPL print commands).

The following optional parameters (param0, param1, ...) can be used to provide values for the corresponding placeholders ({0}, {1}, ...) in localization texts.

NOTE◄ Unlike other Localization access methods, the r() function does not provide a definition for a default value that would be assigned as a return value if no entry exists for the given combination of Resource Bundle and Resource Name.

Example: Apply a label for a date value according to the localization for the given Date types

{$with ({/core:Print/printObjects/shp:Shipment/attributes/shp:ShipmentDate/value[@dateType='DISPATCH_DATE']}) {$r(de.lobster.scm.base.bto.attribute.DateType,{@dateType})}: {$date(y-MM-dd,{date/@start})}}

Result: "Dispatch date: 2020-02-19"

  • This r() function call refers to the common resource bundle for all date types to look-up the date type (dateType) specified in the date attribute.


  • Example: Highlighting a value via formatting defined as a localization entry value.

    {$r(textModules,highlight,,{/core:Print/base:User/address/@name1})}

    Result: "►Karola◄"
    ... if the (first) name for the User of session is "Karola" and there is an entry for [textModules,highlight]in the localization that returns the localization text "►{0}◄".

with()

XPath context


{$with({XPathExpression})TemplateExpression}

The with() function resolves an XPathExpression to define a specific element in the print XML as a point of reference for the interpretation of any relative path found within the TempalteExpression of the with() function.

Effectively, a 'base path' common to several XPath expressions will only be featured once within the bracketed header clause '( )', making the TemplateExpression shorter and more transparent. Besides, the use of relative paths in the TemplateExpression will improve the chances of being able to cut & paste content to a different context without adaptations.

Relative paths in XPath expressions may refer to parent levels 'above' the original level provided by the with() function (e.g. by '../'). Besides, it is always possible to refer to any other element within the print XML from within a TemplateExpression using absolute paths.

The TemplateExpression may contain more or less complex expressions, to include further calls of the with() function, which temporarily define a new 'nested' context.

►NOTE◄ If the XPathExpression returns more than one element, only the first 'match' will be used as a context for processing the TemplateExpression. To loop over all nodes returned by an XPath expression, refer to the foreach() function (see below), instead of with().

Example: Structured output of address data from a company account

{$with ({/core:Print/printObjects/base:CompanyAccount/address})
#{@accNumber} (ID {../@id})
{@name1} {@name2} {@name3}
{@street1} {@streetNo}
{@zipcode} {@city}
{$r(de.lobster.scm.localization.Country,{@countryCode})}
}
  • The address element of the company account serves as a common context here, as it contains most of the relevant properties (in XML: attributes).

  • The internal id (id) of the company account is read from a property from the parent level of the address by a relative path (../@id).

  • A r() function (see above) call resolves the localized plain text for the 'Country' identified by the country code from the address (countryCode).

Result: Presentation of the string returned by the resolver in a pop-up of the 'Info' type:
images/download/attachments/169634416/image2020-3-6_17-26-52-version-1-modificationdate-1710864213806-api-v2.png


Example: Similar presentation of addess data from the company attributes (Company type AAL and CNE) of an order:

{$with ({/core:Print/printObjects/ord:Order/attributes/ord:OrderCompanyAddress/value[@companyType='AAL']/company/address})
{$r(de.lobster.scm.base.company.CompanyType,{../../@companyType})}
-------------------------------------
#{@accNumber} (ID {../@id})
{@name1} {@name2} {@name3}
{@street1} {@streetNo}
{@zipcode} {@city}
{$r(de.lobster.scm.localization.Country,{@countryCode})}
}
{$with ({/core:Print/printObjects/ord:Order/attributes/ord:OrderCompanyAddress/value[@companyType='CNE']/company/address})
{$r(de.lobster.scm.base.company.CompanyType,{../../@companyType})}
-------------------------------------
#{@accNumber} (ID {../@id})
{@name1} {@name2} {@name3}
{@street1} {@streetNo}
{@zipcode} {@city}
{$r(de.lobster.scm.localization.Country,{@countryCode})}
}
  • To identify the type of content, the headline of each address section features the localized text of the respective Company type. The entire TemplateExpression can be copied and pasted without any change into a new with() instance, to present another company address using the same format. In the given example, addresses for the company types 'Principal' (AAL) and 'Consignee' (CNE) are explicitly concatenated in this sequence in the expression. A list of all involved companies can be created much more efficiently by using the foreach() function (see example below). However, retrieving all addresses in a loop allows no control over the sequence of company types.

Result:

images/download/attachments/169634416/image2020-3-9_12-38-49-version-1-modificationdate-1710864213810-api-v2.png

foreach()

Loop over XPath elements

{$foreach({XPathExpression}) TemplateExpression}

The TemplateExpression is interpreted in a loop over all elements (if any) returned by XPathExpression. Each iteration of this loop uses another instance from a list of nodes returned by XPathExpression as a context for interpreting any relative XPath expressions found in the TemplateExpression.

The TemplateExpression may contain more or less complex expressions, to include further calls of the foreach() function, which temporarily define a new 'nested' context and set of elements to iterate (see the final example on the right).

Example: Following the outlines of the examples for the with() function (see above), the given address section layout should be used to create a list featuring all companies involved.

{$foreach ({/core:Print/printObjects/ord:Order/attributes/ord:OrderCompanyAddress/value/company/address})
{$r(de.lobster.scm.base.company.CompanyType,{../../@companyType})}
-------------------------------------
#{@accNumber} (ID {../@id})
{@name1} {@name2} {@name3}
{@street1} {@streetNo}
{@zipcode} {@city}
{$r(de.lobster.scm.localization.Country,{@countryCode})}
}

Result: A list of company addresses, as in the previous example, but listing addresses for more company types (if any).


Example: Creating HTML content to define the involvement of the current company with an order by a list of Company type.

<table>{$foreach ({/core:Print/printObjects/ord:Order/attributes/
ord:OrderCompanyAddress/value[company/@id=/core:Print/base:CompanyAccount/@id]})
<tr>
<td>{@companyType}</td>
<td>{$r(de.lobster.scm.base.company.CompanyType,{@companyType})}</td>
</tr>}
</table>
  • Each row (tr) of the table features two columns, populated with the internal key value and the localization of the company type.

Result: (based on an different order from the one used in the examples for the with() function).

Return value of the resolver

Presentation in a browser

<table>
<tr><td>INV</td><td>Bill to party</td></tr>
<tr><td>AAL</td><td>Principal</td></tr>
</table>

using CSS-Style table,td {border:1px solid black}

images/download/attachments/169634416/image2020-3-9_13-58-4-version-1-modificationdate-1710864213813-api-v2.png


Example: As an abstract from the data of an order an HTML list of all addressed contained should be generated, featuring selected address properties as list items (as far as provided).

{$with({/core:Print/printObjects})
{$foreach({.//address|.//contactAddress})
<p>ADDRESS # {@id}
<ul>{$foreach({@*[starts-with(name(),'name')
or starts-with(name(),'street')
or contains('|zipcode|city|countryCode|',concat('|',name(),'|'))]})
<li>{.}</li>}
</ul></p>}
}
  • The with() function call defines the printObjects element as the context for the foreach() loops, to ensure that addresses belonging to the session context (/core:Print/base:User and /core:Print/base:CompanyAccount) do not appear in the list.

  • The outer foreach() loop iterates over all address and contactAddress elements contained in the order and creates one HTML paragraph (<p>) for each address, which identifies this addess by its internal ID (id) followed before the list element (<ul>) is initiated.

  • The address properties are retrieved by a nested foreach() loop, iterating over all attributes (@*) of an address element specified by the iteration of the parent loop, if they match criteria defined in a rather complex predicate expression ([...]). This predicate demonstrates various techniques of specifying a selection of properties by attribute name. From a functional point of view, attributes are accepted, if their name begins with 'name' or 'street' or is found in a list {zipcode, city, countryCode}. Effectively, this rules applies for address properties name1, name2, name3, street1, street2, streetNo, countryCode, zipcode, city. For each of these properties populated within an address, a list item (<li>) featuring the attribute value ({.}) as text is added to the list (<ul>).

Result: (clipped from a larger list)

Return value of the resolver

Display in browser

<p>ADDRESS # 7652<ul>
<li>Vortex Inc.</li>
<li>Tolkienring</li>
<li>25c</li>
<li>DE</li>
<li>27259</li>
<li>Varrel</li>
</ul></p>
<p>ADDRESS # 3715<ul>
<li>FLUX DPA</li>
<li>DE</li>
<li>82343</li>
<li>Pöcking</li></ul></p>
<p>ADDRESS # 3716<ul>
<li>FLUX CNE</li>
<li>DE</li>
<li>82343</li>
<li>Pöcking</li></ul></p>
<p>ADDRESS # 4952<ul>

...

images/download/attachments/169634416/image2020-2-26_11-13-10-version-1-modificationdate-1710864213772-api-v2.png

if()

Condition

{$if(ifExpression) {then thenExpression} [{elseif (elseifExpression) elseifthenExpression}] [{else elseExpression}]}OR alternative simplified syntax: {$if(ifExpression)thenExpression}

The value returned by the ifExpression influences, which of the other expressions included in a function call are evaluated to determine the return value of the if() function. The sole criterion is whether the value returned by an ifExpression matches the string 'true' (non-case sensitive). If that is the case, the 'if' condition is satisfied.

Occasionally, data from the print XML may data immediately meet that requirement, for instance, data of the 'Boolean' type as demonstrated in the first example to the right.

However, in most cases an ifExpression requires the definition of a more or less complex cirterion, to 'digitize' or translate data of other data types into Boolean values using the cmp() function. If required, several Boolean intermediate results can be processed into larger aggregates by logical operators $and(), $or and $not() within an ifExpression.

Procedure to evaluate the return value per case (then – elseif – else):

If the condition specified by ifExpression is satisfied, the parameters of the function call are searched for an expression introduced by the keyword then. If this exists, the contained thenExpression is evaluated to determine the return value of the if() function. If the keyword then is not found at all, the resolver is aborted returning 'no value' – unless the simplefied syntax (see above) is used, where the thenExpression follows the ifExpression immediately and without using a keyword (see the second example on the right).

If the condition specified by ifExpression is not satisfied, the parameters of the function call search for an expression introduced by the keyword elseif. Any expressions introduced by that keyword will be processed in the order of their appearance, by evaluating whether the return value of the contained elseifExpression matches the string 'true'. In that case, the respective 'elseif' condition files as satisfied and the corresponding elseifthenExpression is evaluated to determine the return value of the if() function. If the 'elseif' condition is not satisfied, the evaluation is continued with the next elseifExpression in line (if any).

If all 'elseif' conditions in expressions introduced by elseif are not satisfied, or no such expressions exist, an expression with the keyword else is searched in the function call parameters. If that exists, the contained elseExpression is evaluated to determine the return value of the if() function. Otherwise, the return value is defaulted to an empty string.

NOTE◄ It is technically possible to define expressions with the keywords then and else more than once in the same function call. However, the evaluation procedure described above will – in contrast to elseif – only evaluate the first instance per type.

►IMPORTANT◄ Logical functions inside an ifExpression must be enclosed by curly brackets to ensure a Boolean value is 'resolved' as an intermediate result:

{$if({$or(true,false)})CORRECT} returns 'CORRECT', in contrast to an expression like
{$if($or(true,false))CORRECT} which produces an empty string, because the supposed call of the or() function is treated as a literal to match against 'true', causing the ifExpression to return 'false'.

Example: Name and value tuples for all flag attributes (see Flag type) found in an arbitrary context should be featured in a list, in which flag values should be symbolized by the characters 'X' (true) and 'O' (false).

{$foreach({.//value[@flagValue and @flagType]})
{$if({@flagValue}){then X}{else O}} - {$r(de.lobster.scm.base.bto.attribute.FlagType,{@flagType})}}}

  • The predicate in the XPatch expression of the foreach() function assumes, that any value element in the evaluated context that features two attributes named 'flagValue' and 'flagType' belongs to a flag attribute.

  • By this assumption a flag value (flagValue) should return only values 'true' or 'false' and therefore the XPath expression {@flagValue} can be used as an ifExpression without involving the cmp() function to translate data to a Boolean value.

  • thenExpression and elseExpression inside the if() function call address the respective symbol character as a literal.

  • Afterwards, the localized name of the Flag type (flagType) is looked-up in Localization by the r() function (see above) to complete the output string.

Result: Sample output in a popup listing flag settings for a user account, where three flags exist out of which one is 'checked' (value 'true') and two are not set (but explicitly feature 'false' as a value):

images/download/attachments/169634416/image2020-3-9_15-24-3-version-1-modificationdate-1710864213816-api-v2.png


Example: In addition to the solution in the previous example, the output string should feature a special status text (e.g. 'Contacting prohibited!'), if none of the flags representing consent for a type of contact is set for a user account:

{$if({$not({$or( {.//value[@flagType='SEND_MAIL']/@flagValue},
{.//value[@flagType='SEND_NEWS']/@flagValue})})}){$r(PRIVACY_STATUS,ZERO_SPAM)}}

  • The individual flag values (flagValue) are directly used as arguments of a disjunction by an or() function.

  • The result value of this disjunction is negated by the not() function, because the criterion of the ifExpression should only be passed, if none of the flags is set. As this example uses the simplified syntax (see above), the thenExpression starts immediately after the closing bracket of the ifExpression, omitting the keyword 'then ' and an addtional set of curly brackets. The status text is looked up in localization, where a localization entry for the custom set of bundle/resource keys should be provided.

►NOTE◄ If a flag attribute (or the contained flagValue field) is not found, when the ifExpression is interpreted, the respective argument of the or() function makes an empty string ('') against 'true', which files as 'false'. Whether the flag was not found or explicitly set to 'false', does not make a difference for further evaluations. Since flags in the given context express consent for being contacted in a certain way, the resulting 'opt-in' logic might match user requirements perfectly. To interpret the same flags for 'opt-out' logic instead, the ifExpression would have to be rearranged as follows:

{$if({$and(

{$cmp({.//value[@flagType='SEND_MAIL']/@flagValue},=,false)}, {$cmp({.//value[@flagType='SEND_NEWS']/@flagValue},=,false)})}){$r{PRIVACY_STATUS,ZERO_SPAM}}}

  • The criterion of the cmp() function (see below) ensures, that a value of 'false' is explicitly indicated for each flag value.

►NOTE◄ Since the simplified syntax of the if() function ultimately only causes a 'conditional output' of the then expression, a comparable result can often also be achieved by formulating a comparable logic in the predicate of an XPath expression within a with() function. For an 'opt-out' check in the example, the following approach would also be useful:

{$with({./attributes[ .//value[@flagType='SEND_MAIL' and @flagValue='false'] and .//value[@flagType='SEND_NEWS' and @flagValue='false'] ]}){$r{PRIVACY_STATUS,ZERO_SPAM}}}

The attributes element defining the with() context here is part of the not completely reproduced context of the original expression. The predicate to this element verify, when both flags exist and explicitly return 'false' as a flag value (flagValue).


Example: A text for a message should appear on a personalized basis on the address properties ('Salutation' and 'Last name').

We assume the last name will always be stored in the 'Name 3' (name3) property of an address and only 'MR' and 'MS' (see Salutation) are the only relevant selections for 'Salutation' in the given context.

{$with({/core:Print/base:User/address})
Hello {$if({$cmp({@salutation},=,MR)}){then Mr.}
{elseif ({$cmp({@salutation},=,MS)})Mrs.}
{else User}} {@name3}!
...
}
  • The gender-specific choices for salutation are checked explicitly by if/elseif conditions and coverted to the text defined by thenExpression or elseifthenExpression. In any other case, the elseExpression defines neutralized output ('User').

►NOTE◄ A blank character following one of the keywords then, else or elseif is considered part of the syntax and not part of the following literal. However, the syntax does not require a blank character after the closing bracket after the elseifExpressionliteral. For this reason {then Mr.} and {else User} are correct, where {elseif (...) Mrs.} would add a redundant blank character to the output string before 'Mrs.'.

Results:

Salutation

Notification

MR

images/download/attachments/169634416/image2020-3-11_10-33-10-version-1-modificationdate-1710864213790-api-v2.png

MS

images/download/attachments/169634416/image2020-3-11_10-35-15-version-1-modificationdate-1710864213794-api-v2.png

(no value)

images/download/attachments/169634416/image2020-3-11_10-31-45-version-1-modificationdate-1710864213784-api-v2.png

cmp()

Comparison

{$cmp(leftExpression,relationalOperator,rightExpression[,compareDataType])}

The cmp() function compares return values or literals provided as leftExpression and rightExpression using a specified relationarOperator, and returns 'true' as a string if the condition defined by these parameters is passed (or else: 'false').

Relational operator

Condition for result 'true'

= or ==

leftExpression is equal to rightExpression

<

leftExpression is less than rightExpression

<=

leftExpression is less or equal than rightExpression

>

leftExpression is greater than rightExpression

>=

leftExpression is greater or equal than rightExpression

!=

leftExpression is not equal to rightExpression

The optional comparison data type parameter defines a specific target data type for interpreting the return values of leftExpression and rightExpression when checking the comparison condition.

Compare
data type

Description

Valid content

s (default value)

String comparison

Strings

i

Integer comparison

  • Range: 32-bit integer (Java)
    Min: -2147483648 (-231)
    Max: 2147483647 (231-1)

  • Sign (+/-) as a prefix (optional)

  • followed by digits (0-9)

l

Long comparison

  • Range: 64-bit long (Java)
    Min.: -9223372036854775808 (-263) Max.: 9223372036854775807 (263-1)

  • Sign (+/-) as a prefix (optional)

  • followed by digits (0-9)

f

Float comparison

  • Range +/-3.4e38

  • Sign (+/-) as a prefix (optional)

  • Digits (0-9) of integer part (if any)

  • Decimal point (if required)

  • Digits (0-9) for decimals (if any)

  • Symbol "e" for scientific number format followed by at least one digit (exponent) optionally prefixed by sign (+/-)

d

Double (float) comparison

  • Range +/-1.7e308

  • Sign (+/-) as a prefix (optional)

  • Digits (0-9) of integer part (if any)

  • Decimal point (if required)

  • Digits (0-9) for decimals (if any)

  • Symbol "e" for scientific number format followed by at least one digit (exponent) optionally prefixed by sign (+/-)

►NOTE◄

Unless specified otherwise expressions are compared as strings, which will only yield the expected results under special circumstances when numerical values are compared without an indication of the applicable compareDataType. Basically, an indication of compareDataType is always resommended for comparing numbers, even if an expression without an explicit choice of data type seems to produce plausible results in sample testing.

If a numerical compareDataType is selected, the return values of leftExpression and rightExpression must be acceptable input for the target format, or else the resolver will either FAIL (abort returning 'no value') or the compare result may be unreliable.

In practice the following requirements should be observed (also see the examples on the right):

  1. The string provided by an expression must not be empty. Unless this can be excluded otherwise, the expression should handle this case explicitly (see examples on the right).

  2. The complete string must be compliant with the criteria for 'Valid content' of the selected compare data type. A string like '121.50' will not be converted to a value of 121 for integer comparison but will cause the resolver to FAIL.

  3. The number value derived from the input string must fit the range of the data type.

    • In an integer or long comparison, a value that is too high or too low will cause an ERROR.

    • With a float or double comparison, a 'too high value' is replaced by the upper limit value and a 'too low value' by the lower limit value. Afterwards, the comparison is performed without an ERROR but possibly with an unexpected result.

Date/time values, typically provided in mumerical form (UTC milliseconds), can be handled as numbers by double comparison or by strings comparison, provided the milliseconds are converted to a suitable format using the date() function. Clever choices for the date pattern for a string copmarison will enable much more specific criteria than just checking the relative position of two timestamps on a UTC time line (see examples to the right).

Boolean values, e. g. from flag attributes, can be handled by string comparison with values 'true' and 'false'.

►NOTE◄ If string values from expressions are referred to as 'booleans' in string comparisons, the common use of lower case may not always be granted. In case of doubt it is recommended to run text input with potentially variable case through an and() or or() function to force it to lower case.

Example: A comparison should determine whether two hypothetical properties (required and granted) of a certain element (permission) bear the same Boolean value. The relevant section of a print XML might look like this:

<permission type="00" required="TRUE" granted="true"></permission>

An expression like {$cmp(@required,=,@granted)} would return 'false' for this element, because the strings are not identical. Clearly this interpretation in not beneficial for the task of comparing Booleans. The following expression ensures a not case-sensitive evaluation and returns 'true' with the sample data above:

{$cmp({$or({@required})},=,{$or({@granted})})}

Examples:

Expression

Result

Remarks

{$cmp(XYZ,=,XYZ)}

true

identical literals (incl. upper/lower case)

{$cmp(XYZ,=,xyz)}

false

literals differing in upper/lower case

images/s/-95e2zf/9012/8yg2g7/_/images/icons/emoticons/warning.svg CAUTIONimages/s/-95e2zf/9012/8yg2g7/_/images/icons/emoticons/warning.svg Different interpretation or leading or trailing blanks between literals and return values! Examples:

{$cmp(A ,=, A)}

true

Leading/trailing blanks are trimmed from literals before comparing.

{$cmp( ,=,)}

true

Leading/trailing blanks are trimmed from literals before comparing.

{$cmp({./@name2},=,xyz)}

false

Assupmtion: Property name2 returns a string 'xyz ' (without qoutes)

  • Leading/trailing blanks are trimmed from literals before comparing.

  • Blanks are not trimmed if literals are enclosed in double quotes. Values are compared without the double quotes (here: rightExpression).

  • Return values of expressions are never trimmed.

{$cmp({./@name2},=, xyz )}

false

{$cmp({./@name2},=," xyz ")}

true

{$cmp({./@name2},=,xyz)}

false

images/s/-95e2zf/9012/8yg2g7/_/images/icons/emoticons/warning.svg CAUTIONimages/s/-95e2zf/9012/8yg2g7/_/images/icons/emoticons/warning.svg Strings representing numbers are compared as strings, unless a numeric compareDataType is indicated:

{$cmp(10,<,2)}

true

A string like '10' is considered 'less' than '2' (string sorting).

{$cmp(10,<,2,i)}

false

compareDataType 'i' compares integer numbers 2 and 10.

{$cmp(1.2,>=,1)}

true

The string '1.2' in considered 'greater' than '1' (string sorting). This result matches the result of numeric comparison (see below) accidentally.

{$cmp(1.2,>=,1),d}

true

With compareDataType 'd' the double values for 1.2 and 1.0 are compared.

images/s/-95e2zf/9012/8yg2g7/_/images/icons/emoticons/warning.svg CAUTIONimages/s/-95e2zf/9012/8yg2g7/_/images/icons/emoticons/warning.svg If the return value of an expression is not compatible with the choice for compareDataType (see column 'Valid content' to the left), the resolver will fail and abort returning 'no value'.

{$cmp(1.2,>=,1),i}

FAIL

'1.2' will not convert to a value of 1 with compareDataType 'i'.

{$cmp(4009900508384,<,4009900600000,i)}

FAIL

EAN codes are 'out of range' for compareDataType 'i'.

{$cmp(4009900508384,<,4009900600000,l)}

true

compareDataType 'l' enables long comparisons with the same data, as the numbers are within the range for long numbers.

{$cmp({./@textValue},<,4009900600000,l)}

false

Assumption: {./@textValue} returns string '4009900601234'

FAIL

Assumption: {./@textValue} returns an empty string ('')

{$cmp(0{./@textValue},<,4009900600000,l)}

true

Assumption: {./@textValue} returns an empty string ('')

The '0' character used as a literal prefix in leftExpression ensures a default value of '0', in case the following expression should return an empty string, because the property textValue does not exist or is empty. If this property is populated with a string of digits instead, the 'leading 0' will not influence the numeric interpretation in a long comparison, as long as the property does not feature a sign symbol as a prefix. Concatenations like '0-4009900508384' or '0+4009900508384' would cause the resolver to fail in case of a long comparison.

{$cmp({$if({$cmp({./@textValue},=,)})
{then 20}{else {./@textValue}}},>,-273,l)}

false

Assumption: {./@textValue} returns a string, that can be interpreted as a positive or negative long value (>-273).

true

Assumption: {./@textValue} returns a string, that can be interpreted as a positive or negative long value (>-273) or is empty. Here an empty string is defaulted to "20" for the long comparison.

images/s/-95e2zf/9012/8yg2g7/_/images/icons/emoticons/warning.svg CAUTIONimages/s/-95e2zf/9012/8yg2g7/_/images/icons/emoticons/warning.svg As a rule of thumb, the limited precision for representing decimal numbers in float format may influence comparison results for numbers differing down from the eighth significant digit in a float comparison, whereas a double (float) copmarison limits allows precise comparisons regarding up to 16 significant digits.

{$cmp(1,=,1.00000001,f)}

true

Numbers in this float comparison are not recognized as 'unequal' despite a decimal difference of 10-8 , since both decimal values map to the same float representation.

{$cmp(1,=,1.0000001,f)}

false

A difference of 10-7 in a similar float comparison files as 'unequal'.

{$cmp(987654321.0123456,=,987654321.0123457,d)}

false

A mismatch in the 16th significant digit is recognized as 'unequal' in this double comparison.

{$cmp(987654321.01234567,=,987654321.01234568,d)}

true

A mismatch in the 17th significant digit is not recognized as 'unequal' in this double comparison.

Examples for evaluation date/time indications (in UTC milliseconds) in comparisons

{$cmp({./@lastModified},>,{./@created},d)}

true

Assumption: The element (e.g. a business object) in the context of this comparison of timestamps for properties 'created' and 'lastModified', was saved at least once (i.e.'changed') since it had been created.

{$cmp( {$date(y-MM-dd,{./@lastModified})},=, {$date(y-MM-dd,{./@created})},s)}

true

Assumption: The timestamps for 'created' and 'lastModified' of the evaluated element point to the same calendar day in the system's default time zone.

{$cmp({$date(HHmm,{./@created};UTC)},<,0730)}

true

Assumption: The timestamp for 'created' points to a time of day before 07:30 UTC (regardless of the calendar date)

and()

Logical conjunction

{$and([Expression[,Expression[,...]]])} 

The and() function defines a logical conjunction (AND-junction) between the values of all Expression arguments, i.e. if all Expressions return a value matching the string 'true' (not case-sensitive), and() returns 'true' and in any other case 'false'.

Example: Xpath expressions and functions may replace any of the literal arguments used in these examples for better transparency.

Expression

Result

Remarks

{$and(true,true,FALSE,true)}

false

A single argument not matching 'true' forces the overall result to 'false'.

{$and()}

true

Calling and() without any argument returns 'true'.

{$and(true,)}

false

An empty argument does not match 'true', so the result is 'false'.

{$and(true,TRUE,tRue,True,truE)}

true

Matching against 'true' is clearly not case-sensitive.

{$and(true,treu)}

false

Any string not matching 'true' is considered 'false'.

or()

Logical disjunction

{$or([Expression[,Expression[,...]]])} 

The or() function defines a logical disjunction (OR-junction) between the values of all Expression arguments, i.e. if only one Expression returns a value matching the string 'true' (not case-sensitive), or() returns 'true' , otherwise: 'false'.

Example: Xpath expressions and functions may replace any of the literal arguments used in these examples for better transparency.

Expression

Result

Remarks

{$or(false,false,TRUE,false)}

true

A single argument matching 'true' forces the overall result to 'true'.

{$or()}

true

Calling or() without any argument returns 'true'.

{$or(true,)}

true

An empty argument does not match 'true'.

{$or(true,false,TRUE,false,fAlsE)}

true

Matching against 'true' is clearly not case-sensitive.

{$or(false,treu)}

false

Any string not matching 'true' is considered 'false'.

not()

Logical negation

{$not(Expression)}

The not() function negates a Boolean value returned from the Expression. If this values matches the string 'true' (not case-sensitive), not() returns 'true' , otherwise: 'false'.

Example: Xpath expressions and functions may replace any of the literal arguments used in these examples for better transparency.

Expression

Result

Remarks

{$not(tRuE)}

false

Matching against 'true' is clearly not case-sensitive.

{$not(FALSE)}

true

{$not()}

FAIL

In contrast to other logical functions not() fails (and the resolver aborts returning 'no value') if no Expression is provided.

{$not(me)}

true

Any string not matching 'true' is considered 'false' which forces 'true' as a result.

{$not({./@name4})}

true

Assupmtion: Expression returns an empty string, because XPath does not return an element or the element returns an empty value.

An empty string does not match 'true', so not() returns 'true'.

substr()

Substring

{$substr(startIndex,maxLength,Expression)} 

The substr() function extracts a defined section of the string returned by Expression. The other arguments define the position of the first character to return and the (maximum) length of the returned substring. These argument values cannot be defined by expressions, but require static numbers (of data type integer):

  • The value for startIndex defines the position of the first character to return. Integer values between 0 and the length of the string returned by Expression are accepted. As an index of 0 identifies the first character of the string returned by Expression, a startIndex equal to the length of that string points to the position after the last returned character, so an empty string is returned. A startIndex out-of-range causes susbtr() to FAIL and the resolver to abort returning 'no value'.

  • maxLength indicates the maximum number of characters (starting from startIndex) to extract from the Expression string. The returned string will at max terminate with the final character of the Expression string. maxLength accepts positive integer values as well as 0, which always returns an empty string. Negative values for maxLength cause susbtr() to FAIL and the resolver to abort returning 'no value'.

►NOTE◄ Internally an 'endindex' calculation adds startIndex + maxLength, so this value must fit the number range for integer values (max. 231-1 = 2147483647).

Example: The following examples are designed for the context of an address element matching the following print XML excerpt:

... <address id=... name1="♥ Karola" name2="" name3="Mustermann"> ... </address> ...

We assume the following expressions are executed in a with() context returning this address element:

Expression

Result

Remarks

{$substr(2,1,{@name1})}

'K'

The initial letter of the first name in property name1 is extracted from the third position of (by startIndex 2) of the string, since in this special case the first name uses a prefix consisting of a symbol (♥) followed by a blank.

{$if({$cmp({$substr(1,1,{@name1})},=," ")})
{then {$substr(2,999,{@name1})}}
{else {@name1}}
}

'Karola'

Since an informal prefix like the ♥ symbol should not appear in an address section, this expression conditionally removes the substr()-function, if another call of substr() extracts a blank as the second character of the first name.

►NOTE◄ Since literal strings used as argument of a function are always automatically trimmed by parser, it is important to protect the blank character inside the cmp() function call by enclosing it in double quotes.

{$substr(0,1,{@name2})}

''

An attempt to extract an initial from the empty name2 property returns an empty string.

Next, a loop over all three name propterties shall be used to extract initials, to generate a personal acronym. In this loop the conditional expression introduced above for removing pefix-symbols should be applied individually for each name property.

{$foreach({@*[starts-with(name(),'name')]})
{$if({$cmp({$substr(1,1,{.})},=," ")})
{then {$substr(2,1,{.})}}
{else {$substr(0,1,{.})}}}
}

FAIL

This expression will fail if one of the name properties is empty (here: name2), since in this case the substr()-call (highlighted in red ink) uses a startIndex of 1 which is larger than the length of the string (0). The following example demonstrates how this problem can be handled.

{$foreach({@*[starts-with(name(),'name')]})
{$if({$cmp({$substr(1,1,{.}#)},=," ")})
{then {$substr(2,1,{.})}}
{else {$substr(0,1,{.})}}}
}

"KM"

This expression returns the expected concatenation of initials from all components of a name. In contrast to the previous example, the critical substr() call uses an arbitrary character as a suffix (here: #) to the expression resolving the property. Consequently, instead of an empty string, the suffix character is returned. Extracting the second character from this string will return an empty string ('') which does not match the blank character checked in the condition, so the else-clause is processed, where the then-clause would again FAIL with a string of length 1.

bsh()

bsh script

{$bsh(scriptExpression)} 

The scriptExpression is executed as bsh script and the interpreted value is returned as a string.

images/s/-95e2zf/9012/8yg2g7/_/images/icons/emoticons/warning.svg WARNINGimages/s/-95e2zf/9012/8yg2g7/_/images/icons/emoticons/warning.svg This function is indented for expert users with programming skills. Therefore, we do not provide detailed application examples for this is part of the developer documentation. This function is reserved exclusively for those problems that cannot be solved by the generic approaches provided for in Lobster Data Platform / Orchestration. Incorrect use of this function can cause considerable and irreparable damage to the system and to the data base. For further information and eventual training/workshops on this function, please contact our support staff (support.pro@lobster.de).