What’s new in EFX 2
| This is a pre-release version of this document. The eForms SDK 2.0.0 is still under development. This notice will be removed when the document is finalized. |
EFX-2 is the version of EFX released with SDK 2.0.0. EFX-2 has two distinct language flavors — EFX Templates and EFX Rules — both built on the shared EFX Expressions syntax which they extend for their specific applications.
Backward compatibility
EFX-2 contains new features that are not supported by EFX-1. Most EFX-1 expressions are still valid EFX-2 expressions, but several changes require modifications:
-
Sequence literals now use
[…]instead of(…). -
Codelist identifiers now use
[…codelistName]instead of(codelistName). The#codelistNamenotation is still supported but will be removed by beta.1. -
The
measuretype has been renamed toduration, and related functions have been renamed accordingly (add-measure→add-duration,subtract-measure→subtract-duration). -
XPath axis specifiers have been removed.
-
Cross-notice references (
notice(…)) have been removed, replaced by dynamic API functions in EFX Rules. -
The EFX-1 expression syntax (
{context} ${expression}) is still supported but will likely be removed by beta.1 in favour ofWITH context COMPUTE expression. -
The EFX-1 template line syntax (
{context} template) is still supported but will likely be removed by beta.1 in favour ofWITH context DISPLAY template;.
EFX Expressions
EFX Expressions are the shared building blocks used in both EFX Templates and EFX Rules.
WITH ... COMPUTE syntax
Expressions can now be written as WITH context COMPUTE expression, improving readability over the EFX-1 {context} ${expression} syntax.
// EFX-1 style:
{BT-23-Procedure} ${BT-23-Procedure in #main-activity}
// EFX-2 style:
WITH BT-23-Procedure COMPUTE BT-23-Procedure in [...main-activity]
Improved type safety
The grammar has been reorganised to enforce a stricter distinction between scalar and sequence types. Ambiguities in late-binding have been removed. Variables and parameters can now be declared with sequence types using the type* notation:
// Sequence parameter declaration
text*:$codes
// Sequence variable declaration
text*:$lot-ids = [for text:$id in BT-137-Lot return $id]
Working with sequences
Sequence literal syntax now uses […] instead of (…) to remove ambiguity with parenthesised expressions:
// EFX-1 style:
BT-23-Procedure in ('works', 'supplies', 'services')
// EFX-2 style:
BT-23-Procedure in ['works', 'supplies', 'services']
The codelist expansion operator has been changed to […codelistName] for consistency. The #codelistName notation is still supported but will be removed by beta.1.
// Legacy notation (to be removed):
BT-23-Procedure in #main-activity
// New codelist expansion:
BT-23-Procedure in [...main-activity]
New conditions allow testing sequences directly:
-
sequence is [not] empty— tests whether a sequence has any elements. -
sequence has [no] duplicates— tests whether all elements in a sequence are distinct. -
expression is [not] unique in sequence— tests whether a value appears exactly once in a sequence.
Iterations now support a DISTINCT modifier to deduplicate results, and can return sequences to concatenate results:
// Deduplicated iteration results:
for text:$code in BT-263-Procedure return distinct $code
// Returning sequences from iterations (concatenation):
// Each lot contains multiple additional classifications (repeatable node),
// so accessing BT-263-Lot returns a sequence that gets concatenated.
for context:$lot in ND-Lot return $lot::BT-263-Lot
Node presence testing
Presence conditions (is present / is not present) can now be applied to node references in addition to field references:
ND-Lot is present
ND-Part is not present
Field context indexing
Field contexts now support indexers, allowing expressions to select a specific occurrence of a repeatable field context by position:
// Access the second lot's procurement scope:
ND-Lot[2]::BT-262-Lot
Field properties
EFX-2 introduces the ability to access properties of a field using the field:property syntax. Three categories of field properties are available:
Privacy controls
Expressions can access privacy-related properties of fields withheld from publication. These properties allow rules and templates to reason about the privacy status of fields without having to reference the underlying privacy fields (e.g. BT-197, BT-198) by name:
-
field:privacyCode— the privacy code of the field. -
field:publicationDate— the publication date for a withheld field. -
field:justificationCode— the justification code for withholding. -
field:justificationDescription— the justification description. -
field:wasWithheld,field:isWithheld,field:isWithholdable,field:isDisclosed,field:isMasked— boolean properties indicating privacy status.
// Access the publication date of BT-105-Procedure without referencing BT-197 directly:
BT-105-Procedure:publicationDate
// Access the justification code:
BT-105-Procedure:justificationCode
New built-in functions
Working with numbers
min, max, average — aggregate functions on numeric sequences.
absolute — absolute value.
round, round-down, round-up — rounding functions.
Working with strings
substring-before, substring-after — split strings around a delimiter.
normalize-space — collapse and trim whitespace.
trim, trim-left, trim-right — whitespace trimming.
pad-left, pad-right — string padding.
repeat — repeat a string N times.
replace, replace-regex — string replacement (literal and regex).
split — split a string into a sequence by delimiter.
url-encode — URL encoding.
capitalize-first — capitalize the first character.
index-of-substring — find the position of a substring within a string.
empty — test whether a string is empty.
Working with dates, times and durations
year, month, day — extract components from a date.
hours, minutes, seconds — extract components from a time.
years, months, days — extract components from a duration.
current-date, current-time — return the current date or time.
measure renamed to duration
The measure type in EFX-1 was only used for durations, which are a special case because they carry the unit in their value (e.g. P10D) to allow calculations. Renaming to duration frees the measure keyword for other types of measure that can be treated as simple numbers. Related function names have also been updated: add-measure → add-duration, subtract-measure → subtract-duration.
EFX Templates
The template language has been enhanced in three areas: more expressive template logic, better reuse and maintainability, and richer output capabilities.
Template syntax and logic
EFX-2 template syntax
Template lines now use WITH context DISPLAY template; instead of the EFX-1 {context} template syntax. Template lines are now semicolon-terminated, and the context declaration is optional — allowing template lines that display content without iterating over a context.
// EFX-1 style:
{BT-22-Procedure} #{name}: $value
// EFX-2 style:
WITH BT-22-Procedure DISPLAY #{name}: $value;
// Without context (static content):
2 DISPLAY #{auxiliary|text|procedure};
The EFX-1 syntax is still supported for backward compatibility but will likely be removed by beta.1.
Reuse and maintainability
Callable templates
Templates can be defined once and reused across the template file:
// Declaration:
LET template:role-and-name(text:$label-id, text:$org-tpo-id)
WHEN $org-tpo-id in OPT-200-Organization-Company
DISPLAY #{${$label-id}}: ${BT-500-Organization-Company[OPT-200-Organization-Company == $org-tpo-id]:preferredLanguageText}
OTHERWISE
DISPLAY #{${$label-id}}: ${BT-500-Organization-TouchPoint[OPT-201-Organization-TouchPoint == $org-tpo-id]:preferredLanguageText};
// Invocation:
WITH OPT-301-Lot-ReviewOrg
INVOKE role-and-name('auxiliary|text|organisation-review', OPT-301-Lot-ReviewOrg);
EFX Rules
EFX-2 introduces a complete rules language, allowing business rules to be authored directly in EFX.
Rules file structure
A rules file consists of optional global declarations followed by one or more validation stages:
#include "stage-1a.efx"
---- STAGE 2a ----
WITH ND-AuctionTerms
ASSERT BT-122-Lot is not present
AS ERROR R-J6A-ZY4
FOR BT-122-Lot
IN 1-6, 15, 23-40, CEI, E1-E2, E4-E6, T01-T02, X01-X02
ASSERT BT-767-Lot is present
AS ERROR R-LTD-XCL
FOR BT-767-Lot
IN 16-18, 22, 29-31
Dynamic rules
Dynamic rules allow validation to consider information retrieved from other notices through a specialised API. This replaces the EFX-1 cross-notice reference syntax (notice(…)) which was impractical and never used.
ENDPOINT "ted-api" AT "https://api.ted.europa.eu/v1";
LET dynamic:?checkPreviousNotice(text:$noticeId) CALL API "ted-api" ON ERROR WARN;
-
API endpoint declarations:
ENDPOINT "name" AT "url"for named API endpoints, overridable at runtime. -
Dynamic/API functions:
LET dynamic:?name(params) CALL API "endpoint"withON ERROR WARN|REJECTclauses.
Rule syntax
Rule-sets
Multiple rules can share the same context via a single WITH clause, avoiding repetition of context declarations.
ASSERT and REPORT
Two rule types with distinct semantics:
-
ASSERTdefines a condition that must be true for the rule to pass. The rule fires (reports a violation) when the condition is false. -
REPORTdefines a condition that, when true, triggers a report.
Conditional rules
Rules can be conditionally applied using WHEN / OTHERWISE:
WITH ND-Root
WHEN ND-Lot is present
ASSERT BT-137-Lot is present
AS ERROR R-ABC-123 FOR BT-137-Lot IN 7-40
OTHERWISE
ASSERT ND-Part is present
AS ERROR R-ABC-124 FOR ND-Part IN 4-6, E2;
Severity and rule ID
Each rule specifies a severity (ERROR, WARNING, INFO) and an identifier following the R-XXX-XXX pattern, used for error message translation lookup:
AS ERROR R-001-001
AS WARNING R-002-003
AS INFO R-003-001
FOR clause
FOR field|node specifies which field or node the rule validates. This is used to organise validators by target for efficient lookup.
Notice type targeting
IN specifies which notice types the rule applies to. Supports individual types, ranges, and wildcards:
IN 1-3, 10-13, E1-E3 // Explicit ranges
IN * // All notice types
IN ANY // All notice types
Scope annotations
SCOPE specifies rule applicability and categorisation:
-
@PRE— rule applies only during live validation (while editing). -
@POST— rule applies only after submission. -
#flag— categorisation flag (e.g.#lawfulness), carried over to SVRL output.
Omitting @PRE/@POST means the rule applies in both contexts.
Shared between EFX Templates and EFX Rules
Include directives
Template and rules files can be composed from fragments using #include "path/to/file.efx".
Variable declarations
LET type:$var = expression for declaring typed variables (both scalar and sequence types), available as global declarations in templates and as global or stage-level declarations in rules.
Context declaration with inline variables
The context declaration block (WITH) now supports local variable declarations before and/or after the context, allowing variables to be computed and reused within the scope of a template-block or rule-set.
WITH ND-Organization,
text:$orgid = OPT-200-Organization-Company,
text*:$tpoid = OPT-201-Organization-TouchPoint
DISPLAY ${BT-500-Organization-Company:preferredLanguageText};