1. What is JMESPath?

JMESPath is a query language designed specifically for JSON data. It allows for the selection and transformation of elements within a JSON structure in a standardized and efficient way. This makes JMESPath particularly well-suited for use in modern data environments, such as web APIs and NoSQL databases, where JSON is a commonly used data format.

JMESPath is supported in a wide range of programming languages. A list of available libraries can be found at jmespath.org/libraries.

To test expressions, the following tools are recommended:

2. Use of JMESPath within the AFD-definition Standard

Within the AFD-definition Standard, JMESPath is used to define validation rules for AFD 2.0 messages. These rules check dependencies between multiple data elements in a single message. Each rule must be expressed as a conditional expression that returns either true or false.
This structure ensures that validation rules are both machine-readable and consistent across implementations.

A validation rule in AFD 2.0 consists of:

  • A unique ID for the rule
  • A test: the actual condition, expressed as a JMESPath expression
  • An optional message to be shown if the rule fails
  • An optional source with documentation or references

3. Structure of a validation rule

A validation rule typically follows this logical pattern:

if (condition A) then (condition B) else true

This means:

  • Condition A defines when the rule should apply.
  • Condition B defines what must be true if condition A is met.
  • If condition A is not met, the rule automatically returns true, and no further evaluation is needed.

4. Translating validation logic into JMESPath

As seen above, validation rules in the AFD-definition Standard are conceptually structured as if A then B else true. This means that B must be true if A is true; otherwise, the rule passes by default. In JMESPath, this structure is not expressed using explicit if/then/else syntax. Instead, it is reformulated as a logical expression that must return either true or false.

Logical structure in JMESPath

The pattern ‘if (condition A) then (condition B) else true’ is translated in JMESPath to:

!A || B

This reads as: if condition A is not true, the rule is satisfied; if A is true, then condition B must also be true.

Interpreting the result: true or false

A validation rule returns:

  • true: the rule is satisfied (either because condition A does not apply, or because condition B applies).
  • false: the rule is violated (condition A applies, but condition B does not).

This binary outcome is suitable for automated validation and consistent interpretation.

5. Applying JMESPath syntax in validation rules

In this section, we explain how to build validation rules using JMESPath syntax. Each step focuses on a specific aspect of the syntax, with an example drawn from real AFD data structures. The aim is to help readers understand how to translate business logic into correct and readable JMESPath expressions.

5.1 Filtering arrays

AFD messages are structured as arrays of entities (e.g. multiple coverage or object items). To select specific items, JMESPath uses a filter expression with ?.

Example: Select all hull vehicle coverages:

policy[].coverage[? entityType == 'hullVehicle']

This returns a list of all coverage entities where entityType equals ‘hullVehicle’.

5.2 Combining conditions with && and ||

You can filter more precisely by combining multiple conditions using:

  • && for AND
  • || for OR

Example: Select coverages of type hullVehicle with coverageCode 2002 or 2003:

policy[].coverage[? entityType == 'hullVehicle' && (coverageCode == `2002` || coverageCode == `2003`)]

5.3 Checking whether an item exists using [0] == null

To check whether a matching item exists or not, compare the first item in the filtered list to null.

Example: Check if there is no motor vehicle with dealerExtrasValue ≥ 2000:

policy[].object[? entityType == 'motorVehicle' && dealerExtrasValue >= `2000` ][] | [0] == null

This returns true if such an object does not exist.
Note: the value 2000 is wrapped in backticks (`) to ensure numeric comparison.

5.4 Applying the !A || B pattern

To implement conditional logic (if A then B), use the pattern:

!A || B

This ensures that:

  • If A does not apply, the rule passes (true)
  • If A does apply, then B must also be true

Example: “If there is a hull vehicle with coverageCode 2002 or 2003 and there is a motor vehicle with dealerExtrasValue ≥ 2000, then the deductibleAmount for the hull vehicle must be ≥ 750.”

Translated to JMESPath:

!(policy[].object[? entityType == 'motorVehicle' && dealerExtrasValue >= `2000` ][] | [0] != null)
||
(policy[].coverage[? entityType == 'hullVehicle' && (coverageCode == '2002' || coverageCode == '2003') && deductibleAmount >= `750` ][] | [0] != null)

5.5 Comparing numbers

In JMESPath, string values are enclosed in quotes (‘value’) and numeric values in backticks (`value`). To compare numbers correctly, always write numeric constants with backticks:

amount > `1000`

6. Example

The following example demonstrates a validation rules written in JMESPath, including the standard structure used within the AFD-definition Standard. Each rule is shown as a JSON object with four elements:

  • id: the identifier of the rule
  • test: the JMESPath expression
  • message: a description of the validation rule
  • source: a reference to documentation or context

Each example is followed by a short explanation of when the rule returns true (valid) or false (invalid).

Example

The following validation rule ensures that if there is a coverage with entityType hullVehicle, then there must also be an object with entityType motorVehicle and an initialListPrice greater than 0:

"rule": [
  {
    "id": "Verbandscontrole 1",
    "test": "!((policy[].coverage[? entityType == 'hullVehicle'])[] !=`[]`) || ((policy[].object[? entityType == 'motorVehicle' && initialListPrice > `0`])[] !=`[]`)",
    "message": "In case there is a coverage for hull vehicle, there must be a motor vehicle and the initial list price must be greater than '0'.",
    "source": "Manual par 1.4"
  }
]

This validation rule returns true in the following situations:

  • The message contains a coverage entity with entityType hullVehicle, and it also contains an object entity with entityType motorVehicle with an initialListPrice greater than 0.
  • The message does not contain any coverage entity with entityType hullVehicle (i.e. condition A is false).

The rule returns false if:

  • The message contains a hullVehicle coverage, but no motorVehicle object is present.
  • The message contains a motorVehicle object, but the initialListPrice is missing or not greater than 0.

Note: the numeric value 0 is enclosed in backticks to indicate a number, not a string.

7. Guidelines for the composition of AFD 2.0 validation rules

The table below provides key guidelines for creating validation rules using JMESPath within the AFD-definition Standard.

Name Description
Conditional expression Each validation rule must be a conditional expression that returns either true or false. The structure typically follows the logic: !A || B, meaning: “if condition A applies, then condition B must be met; otherwise, the rule passes.”
First condition (A) Defines when the rule should apply, based on the presence or value of certain data. Example: policy[].coverage[? entityType == ‘hullVehicle’] [] | [0] != null) checks whether at least one hullVehicle coverage exists.
Second condition (B) Describes what must be true if condition A is met. Example: policy[].object[? entityType == ‘motorVehicle’ && initialListPrice > ‘0’ ] [] | [0] != null checks that there is at least one motorVehicle with a valid initialListPrice.
Logical operator Use || (logical OR) to combine the two conditions: !A || B.
This ensures the rule returns true if either A does not apply, or B is valid.
This structure is logically equivalent to: if A then B else true.
Use && (AND) within a condition to require that multiple conditions all apply.
Arithmetic operators JMESPath supports common operators such as >, <, >=, <=, ==, !=, &&, ||, and !. These can be used directly, without escaping.
Backticks Always use backticks (`) for numeric values in comparisons. This ensures they are treated as numbers rather than strings.
Null AFD 2.0 works with arrays of entities, causing many JMESPath filters to return lists (even if only one match is expected). To test whether no matching item exists within an AFD 2.0 message, select the first element and compare it to null, for example: policy[].object[? entityType == ‘motorVehicle’ && dealerExtrasValue >= `2000` ] [] | [0] == null. This expression returns true if no object entity of entityType motorVehicle meets the condition.
This pattern is widely used in AFD validation rules to determine whether a condition is not met, based on the absence of valid data.

8. Location and format

All validation rules for AFD 2.0 are stored in the file validationRules.json. Each rule appears as a JSON object containing the fields described above. This makes validation rules easily portable and integrable in various technical environments.

9. Use of AFD custom functions in JMESPath

9.1 Why custom functions?

JMESPath includes many built-in functions, such as abs, avg, starts_with, and length. However, some validations required in the AFD-definition Standard go beyond what is possible with these standard functions. To meet these needs, custom functions can be added to JMESPath.
These custom functions can be implemented using JMESPath libraries in programming languages like Python or .NET. A list of compatible libraries is available at jmespath.org/libraries.
Please note: custom functions are not recognized by general online JMESPath testers, as they are not part of the standard JMESPath language. The available standard libraries for JMESPath can handle custom functions, but users have to set up their own test environment to test these functions.

9.2 AFD custom functions

To avoid every party developing their own versions of custom functions, SIVI provides standard custom functions within the AFD-definition Standard: AFD custom functions. These functions are developed to cover missing functionality in the JMESPath standard in a consistent way.
An AFD custom function is a function predefined by SIVI that you use within a JMESPath expression, but that is not part of the standard functions. These functions are specifically tailored for use within the definitions of AFD 2.0.

Each AFD custom function comes with a formal specification including:

  • Name
    Name of the function in camelCase, should be self-explaining about the purpose of the function.
  • Signature
    Formal function call, including types. Example: number acfCountDateDiff(string subjectDate, string referenceDate, string unit)
  • Input parameters
    For each parameter the following is defined: the name, the data type (such as string or number), the expected format (such as ‘YYYY-MM-DD’), an explanation of the meaning and code lists if applicable.
  • Return value
    Description of the type, the expected format and the interpretation of the result.
  • Examples
    Input with corresponding expected output, for example: acfCountDateDiff(‘2024-01-01’, ’2024-01-10’, ‘days’), result = ‘9’

If a needed function is not yet available, users are encouraged to contact SIVI. If the required logic is sufficiently generic, SIVI will consider developing a standard AFD custom function.

9.3 Overview of available AFD custom functions

Below is a summary of the AFD custom functions currently available. Click on a function to see more details.

acfCountDateDiff

Calculates the number of whole units (year, month, week, day) between two dates. The result is truncated and not rounded.

Signature:
number acfCountDateDiff(string $subjectDate, string $referenceDate, string $calendarUnit)

Input parameters

  • subjectDate
    Startdate (YYYY-MM-DDDD) of the calculation
  • referenceDate
    Date that is checked
  • calendarUnit
    Unit of the difference between the two dates: ‘day’, ‘week’, ‘month’ of ‘year’

Example:

birthDate.acfCountDateDiff(@, '2024-07-07', 'year') >= `18`

This checks whether a person is at least 18 years old on 7 July 2024.


acfDateAdd

Adds a number of time units (positive or negative) to a given date, returning the resulting date.

Signature:
string acfDateAdd(string $subjectDate, number $number, string $calendarUnit)

Example:
effectiveDate.acfDateAdd(@, -2, ‘year’)
This subtracts two years from the effective date and returns the result.


acfSubString

Extracts a number of characters from a string, starting at a given position.

Signature:
string acfSubString(string $subjectText, number $startCharacter, number $numberOfCharacters)

Example:

licensePlate.acfSubString(@, 1, 3)
This results the first three characters of the license plate.

acfTrim, acfTrimLeft, acfTrimRight

Removes specified characters (or spaces by default) from a string.

Signatures:

  • string acfTrim(string $subject [, string $chars])
  • string acfTrimLeft(string $subject [, string $chars])
  • string acfTrimRight(string $subject [, string $chars])

Example:

!starts_with(licensePlate.acfTrimLeft(@, '1234567890'), 'B')

This checks whether the first letter of the license plate (after removing any leading digits) is not a ‘B’.

Feedback

Thanks for your feedback.

Post your comment on this topic.

Please do not use this for support questions.
If you have any support questions, do not hesitate to contact us.

Post Comment