STU3 Candidate

This page is part of the FHIR Specification (v1.8.0: STU 3 Draft). The current version which supercedes this version is 5.0.0. For a full list of available versions, see the Directory of published versions . Page versions: R5 R4B R4 R3

FHIR Infrastructure Work GroupMaturity Level: 0Ballot Status: n/a yet

The FHIR Specification includes a mapping language. The mapping language as a concrete syntax, defined and described in this page, and an abstract syntax, which is found in the StructureMap resource. See also the Tutorial.

The mapping language describes how one set of Directed Acyclic Graphs (instances) is transformed to another set of directed ayclic graphs. When the instances have formal definitions that are represented as Structure Definitions, the language can use additional type related features, though it can function without them, and therefore can function on any directed acyclic graph, even if it has no typing system (or cannot have one e.g. HL7 v2).

The mapping language addresses 2 very different kinds of transformations:

  • Structural changes between the source and target structures
  • Differences in formats in string (and related) primitives contained within the structures

A map has 5 parts:

  • Metadata
  • References to the structures involved in the mapping
  • Imports: additional Maps used by this map
  • a series of groups, with a list of input variables
  • A series of transformation rules in each group

Maps are executed by a mapping engine. This takes one or more inputs of instances (directed acyclic graphs) and a map, and produces a set of outputs as specified by the map. The exact details of the form that the instances take are a matter for the map engine / application API. This language assumes that the engine can query an element in the instance for it's children, it's primitive value, and (optionally) it's type. The language also assumes that the engine has application support for the following operations:

  • ValueSet validation operation
  • Translation operation
  • Lookup another tree of data
  • Create an instance tree
  • Return the correct string format to refer to a tree (input or output)

Generally, it is assumed the invocation of the engine follows some pattern like this:

  • The host application creates the engine, and passes it a handle to the standard services
  • The host application chooses the model map resource, and asks the engine to prepare it (load, check, cache-up etc)
  • The host application asks the engine to execute and provides a set of resources that match defined inputs in the map
  • Any created output will be created via the standard API

Mapping files are always plain text in unicode. Whitespace is any unicode whitespace, and the particular whitespace used is not signicant, except that unicode end of line characters terminate a comment. Comments are started by the characters "//".

The abstract model includes documentation for each item. The canonical text representation is for each item to be on it's own line, with documentation at the end of the line as a comment.

All names defined by the map language - group, rule and variable names - must be valid id (1-64 characters, upper and lowercase letters, numbers, dashes, dots and underscores), and must start with a letter. The special values 'true' and 'false' are not allowed as variable names.

The first part of the mapping syntax establishes the name of the mapping:

map "[url]" = "[name]"

The letters "map" are the first non-whitespace non-comment characters in the source. This is followed by the canonical URL that identifies the map uniquely, and then a human readable name for the map.

todo: add additional metadata?

The next section of the map references the set of structure definitions that are used or produced by this map.

uses "[url]" as [mode] // documentation

This optional section lists one or more structure definitions that the map makes use of, and indicates for each structure definition, how it is used.

Any kind of structure definition may be referenced, including data types, resources, constraints on those, and logical models.

There are 4 modes in which a structure definition may be used:

  • source: One of more instances of this type are passed to the mapping engine when the mapping is executed, and serve as the source from which mapping is performed
  • queried: The map may ask the (via the API, see above) for some instances of this type. For further discussion, see below
  • target: One or more instances of this type are passed in, and will be populated from the source material
  • produced: The map may ask (via the API, see above) for some instances of this type to be created. For further discussion, see below

The simplest case, which is also relatively common, is where a single structure is converted to another single structure. in this case, the map specifes one target, and one source. Such maps can be used automatically - the host application has content in one format, creates an empty instance of the target, and asks the mapping engine to convert.

However, many mappings are not so simple. For instance, converting from a single CDA document to FHIR typically creates a set of resources. In this case, there is a single target - a Bundle, but it is also useful to specify a set of other structure definitions that may be created as part of the bundle. Alternatively converting from one source model to another might involve looking up other information in other instances of data.

It's also possible for a map not so specify any structure definition dependencies. A map that doesn't indicate any structure definitions can still be used, but the type features of the map language can't be used, and such maps typically require special development to integrate the execution of the map into an application.

This section references additional maps that are used by this map:

imports "[url]" // documentation

Typically, maps that are imported a type based, such as a CDA --> FHIR map that makes use of a CD --> CodeableConcept map.

How imported maps are actually used is discussed below.

Each Mapping source contains one or more groups of rules. Each group defines a set of related mapping rules that take the same input and output variables, that define exactly which instances are passed to the mapping, and provides names by which they may be passed when invoking the map:

group [group-name] (extends [other-group])
  input [name] : [type] as [mode] // documentation

Each group has a name, which is how the mapping is invoked. The first group is special, in that this is the group invoked if there no name is provided (e.g. starting the mapping by a host application). Groups may extend other groups, which means that the rules in the other group also apply (typically, this is used with specialising classes in an OO context).

Each variable parameter to the group has a name. This is the name that applications use when passing the instance to the invocation engine, or that rules use when invoking the group. Inputs may have a type - and should (see the discussion above), but are not required to. Input variables also have a mode, which may be one of source or target (see above).

There must be at least one input variable - else there's nothing to map. Maps may list more that one input variable, where that's necessary. Maps can also list one or more target (output) variables, instances that must exist in order to call the map. Note that a map can specify no output variables, and create output instances based on the content of the input, rather than specifying that they must be created in advance.

The main portion of a map consists of a set of transform rules that describe how source content is transformed into target content. The full format for a rule looks like this:

name_of_rule: for src_context.field as new_variable where condition make tgt_context.field as new_variable = create("RRoot") then by [details].

Each rule has 4 main sections:

  • Name: The identity of the rule, for internal references
  • Source Content: One or more elements from the source that contribute to the mapping
  • Target Transform: One or more specifications of content to create in the target model
  • Dependent Rules: Specifies which - if any - rules or groups to apply within the scope of the rule

Each rule is assigned a name. The name is used when specifying rule links, and in traces (a record generated by the conversion engine recording the transform process). Names must be unique within the context of the map.

Each rule specifies one or more elements taken from the source that define variables that can be used when specifying target content, or re-used in subsequent transform rules. Multiple source elements are separated by a comma, like this:

rule_name: for [source], [source], make ...

Each [source] contains the following items:

for (optional) context.element { list-option } as variable where [fluentpath] check [fluentpath]
  • optional: The keyword 'optional' indicates that the rule should still apply when there is no value that matches this source element - see below
  • context: Either a variable or type that is the context in which this rule applies. Variables are not prefixed, and must be declared elsewhere in the map ('as variable' below, or in the input source variables). If the context is a type, it is prefixed by the character '@' e.g. @Patient.id
  • element: An optional name of a child element of the context. If this is not provided, the source is the context. If this is provided, the rule will apply once for each element on the context that matches this name. If the element name contains spaces (possible in some contexts), it can be quoted using "
  • list-option: by default, the rule will apply once for each occurance of the element in context. The list option can override this to specify only to apply to the first, or the last, and to ignore the others
  • as variable: if an element is specified, a variable name must be assigned. This variable name may be used in the target statement, and may be re-used in other rules that apply in the scope of this rule
  • where [condition]: a fluent path (ref) expression that is evaluated to boolean on the context. If the expression returns false, the source element has no match
  • check [condition]: a fluent path (ref) expression that is evaluated to boolean on the context. If the expression returns false, the mapping engine terminates execution with an error. Note: it's usual to have either a where or a check clause, but possible to have both. If both are provided, the where clause is evaluated first, and the check only applies if the where expression is true

If all the source elements have a match (or are labeled as optional) the rule applies for the permutation of the source elements (e.g. if there are 2 elements, each with 2 matches, the rule applies 4 times, one for each combination). Typically, if there is more than one source element, only one of the elements can repeat.

Once the source statement is evaluated, the engine performing the evaluation has a set of variables, each of which contains a single value. These variables are now mapped into the target structures in the target transformation.

Each rule specifies zero or more elements to be created in the target structure. These targets can also be assigned to variables that can be used in subsequent transform rules. If no targets are specified, there are not created targets, just newly defined source variables. Multiple target elements are separated by a comma, like this:

... make [target], [target] then by...

Each [target] contains the following items:

make context.element = transform_code(parameters...)  as variable {list_modes}
  • context: Either a variable or type that is the context in which the content will be created. Variables are not prefixed, and must be declared elsewhere in the map ('as variable' below, or in the input target variables). If the context is a type, it is prefixed by the character '@' e.g. @Patient.id
  • element: An optional name of a child element of the context. If this is not provided, the transform rule must be 'create' - this means the creation of a new instance of data as part of the output. If this is provided, the created value will be placed into the named element
  • transform+parameters: details how the content that is created is transformed from the source data. See below for possible transform codes. If no transform code is provided, then the element is autocreated. It is an error if auto-created elements are primitive types, or have more than one possible type
  • as variable: a variable name may be assigned, which allows the created item to be re-used in other rules that apply in the scope of this rule
  • list_modes: control how elements that repeat are managed when the transform rule is evaluated

Each time the rule is applied, the engine determines the value from the transforms, considers the list mode, if required, and creates that specified content in the target instance. Within a given transform url, the targets are processed in order, so that a transform rule may refer to a variable defined by a prior transform rule.

The following list specifies that transforms that can be specified. Each transform takes one or more parameters:

NameparametersDocumentation
copy source simply copy the source to the target as is (only allowed when the types in source and target match- typically for primitive types). In the concretet syntax, this is simply represented as the source variable
create type use the standard API to create a new instance of data. Where structure definitions have been provided, the type parameter must be a string which is a known type of a root element. Where they haven't, the application must know the name somehow
truncate source, length source must be some stringy type that has some meaningful length property
escape source, format1, format2 Change the internal escaping of a string element. Note: this is not often needed, as mostly the escaping is done on the base format
cast source, type? cast source from one type to another. target type can be left as implicit if there is one and only one target type known
append source... source is element or string - just append them all together
translate source, map_uri, output use the translate operation. The source is some type of code or coded datatype, and the source and map_uri are passed to the translate operation. The output determines what value from the translate operation is used for the result of the operation (code, system, display, Coding, or CodeableConcept)
reference source return a string that references the provided tree properly
dateOp ?? Perform a date operation. Parameters to be documented
uuid n/a Generate a random UUID (in lowercase). No Parameters
pointer resource Return the appropriate string to put in a Reference that refers to the resource provided as a parameter
evaluate resource Execute the supplied fluentpath expression and use the value returned by that. In the concrete syntax, there is a short hand for this operation, by supplying () around the parameter
cc (text) or (system. Code[, display]) Create a CodeableConcept from the parameters provided
c system. Code[, display] Create a Coding from the parameters provided
qty (text) or (value, unit, [system, code]) Create a quantity. Parameters = (text) or (value, unit, [system, code]) where text =s the natural represenation e.g. [comparator]value[space]unit
id system, value[, type] Create an identifier. where type is a code from the identifier type value set
cp (value) or (system, value) Create a contact details. If no system is provided, the system should be inferred from the content of the value

TODO: explain how optional parameters work with transforms (append only?), document list mode

Once the source elements are evaluated, and any specifed targets created, the engine has a set of variables that represent source and target contexts in which further mapping may occur. The set of variables includes those already defined before the rule was evaluated, as well as those newly created by the evaluation of the rule. For some created elements that are primitive types, that's the end of the road - there's nothing more to do with them. But if either or both the source and target types are complex, there may be additional mapping rules that need to apply to the newly created variables.

Transform rules specify what additional rules are evaluated when the rule is complete, by containing other rules:,

 .. then {
   .. other rules...
 }

When a rule contains other rules, the variables from the containing rules are all available to the contained rules. Alternatively, a rule can nominating another group of rules from the same or an imported mapping. Each rule or group is listed by name, and then a set of parameters are provided.

 .. then rule(param, param)

todo