FHIR Release 3 (STU) 4

This page is part of the FHIR Specification (v3.0.2: (v4.0.1: R4 - Mixed Normative and STU 3). ) in it's permanent home (it will always be available at this URL). 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 R4 R3

FHIR Infrastructure Work Group Maturity Level : 0 (Draft) Ballot Standards Status : Trial Use

The FHIR Specification includes a mapping language. The mapping language has a concrete syntax, defined and described in this page, and an abstract syntax, which is found in the StructureMap resource (and there is an antlr grammar for the concrete syntax ). resource. See also the Tutorial .

The mapping language describes how one set of Directed Acyclic Graphs (an instance) is transformed to another set of directed acyclic graphs. It is not necessary for the instances to have formal declarations and/or be strongly typed - just that they have named children that themselves have properties. On the other hand, when the instances are strongly typed - specifically, when they have formal definitions that are represented as Structure Definitions , the mapping language can use additional type related features.

The mapping language addresses two very different kinds of transformations:

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

A map has 6 parts:

  • Metadata
  • Embedded ConceptMaps to translate between different code systems
  • References to the structures involved in the mapping
  • Imports: additional Maps used by this map
  • A series of groups, each 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 its children, its primitive value, and (optionally) its 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)

These functions constitute a Mapping Support API that makes maps portable between different systems

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 structure map resource, and asks the engine to prepare it (load, check, cache-up etc) 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

Some host applications may be able to determine how to combine maps and inputs on the fly based on their metadata, and require minimal configuration, while others may require manual arrangements in order to manage the map execution process.

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

The abstract model includes documentation for each item. The canonical text representation is for each item to be on its 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 ids (1-64 characters, upper and lowercase letters, numbers, dashes, dots and underscores), dashes and must dots). To avoid parsing ambiguities however, they cannot start with a letter. The special boolean values 'true' character, cannot be one of the keywords used in the language (see section Reserved Keywords below) and 'false' are not allowed as variable names. cannot contain a dot or dash, unless the names can be escaped. Escaping can be done by surrounding them by backticks or double quotes. For example:


   src  document4  "not-found"  "section4.5"  `group` 

The first part of the mapping syntax establishes the name of the mapping: mapping. For example:

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

  map "http://hl7.org/fhir/StructureMap/CodeSystem3to4" = "R3 to R4 Conversions for CodeSystem"

The letters "map" "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. map, quoted in strings.

todo: add additional metadata? Yes, maybe in comments like javadoc or C# xmldoc?

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

uses "[url]" (alias name) as [mode] // documentation

  uses "http://hl7.org/fhir/3.0/StructureDefinition/CodeSystem" alias CodeSystemR3 as source   // documentation
  uses "http://hl7.org/fhir/StructureDefinition/CodeSystem" as target  // 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. It may also provide an Alias alias - a name used for the type inside the mapping language - this may be necessary when transforming from source to target where both source and target use overlapping type names (not unusual). If no alias is given, the name for the type will default to the name given in the StructureDefinition (StructureDefinition.name).

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) 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) for some instances of this type to be created. For further discussion, see below

The simplest case, which is common, is where a single structure is converted to another single structure. in this case, the map specifies one target, and one source. Such maps are easy to use 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 for resources that may be created as part of the bundle. Alternatively 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 to 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 next optional section references additional maps that are used by this map: map. For example:

imports "[url]" // documentation

  imports "http://hl7.org/fhir/StructureMap/*3to4"  // documentation

Typically, maps that are imported are type based, such as Maps can be broken up into several files, each containing a coherent set of groups. For example, when writing mappings for CDA --> FHIR to FHIR, one might have one file to map that makes use of a the main document, and another file containing the mappings for the datatypes (e.g. CD --> CodeableConcept map. to CodeableConcept). How imported maps are actually used is discussed below.

The [url] url in the import statement may contain a "*" "*" as a wildcard character (as shown above) to include any matching maps that are available to the mapping engine.

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

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

group [group-name] (inputs) (extends [other-group]) (<<stereotype>>) { // documentation
  .. rules ..
}  
For example:

  group CodeSystem(source src : CodeSystemR3, target tgt : CodeSystem) extends DomainResource <<type+>>
  {
    // documentation
    .. rules ..
  }

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 no name is provided (e.g. starting the mapping by a host application).

The inputs of a group are also referred to (below) as its input parameters, or just as parameters; or as input variables. variables, and are a comma separated list where each items has the format:

  [mode] [name]( : [type])

Each input 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 have (see the discussion above), but are not required to. Input All input variables also have a mode, which may be one of source or target (see above). Inputs may have a type, but are not required to. There must be at least two input variables (source and target) - else there's nothing to map. Maps map, except for the special case of the first group that may only have a single input. Groups may have additional input or output inputs, where that's necessary.

Groups may extend other groups, which means that the rules in the other group also apply (typically, this is used with specialising specializing classes in an OO context). When a group extends another group, it SHALL have the same input parameters (by mode, name, type and mode) type if specified) though their order may differ, and it MAY have additional parameters.

The key word for is used stereotypes <<types>> or <<type+>> can be added to the end of the group declaration to indicate that this group provides a set of mappings that are intended to be used as the default way to map from source to target. There are 2 variants: for types : Use this by default when a [source] of the specified type must be converted to a [target] of the specified type, and no specific dependent rules (see below) are specified. for type+types : in addition, to the above use, when a [source] of the specified type must be converted to a [target] and the type of For more information, see the target is not fixed, use this group section on "Default mapping groups" below.

In both these cases, the group Default mapping groups SHALL have 2 two parameters, a source, and a target, in that order, and both SHALL have specified types for the inputs.

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([type]) then [details].

src_context.field as new_variable where condition -> tgt_context.field = create([type]) as new_variable then [details] "name";
For example:

    src.value : code as vs0 -> tgt.value = create("code") as vt0 then code(vs0, vt0) "valueCode";

Each rule has 4 three main sections: parts:

  • Name Source Content : The identity initial part of the rule, for logging/debugging Source Content : One rule before the '->'. Specifies one or more elements from the source that contribute to the mapping
  • Target Transform : One Starts after the '->' and consists of zero or more specifications of how to use the source content to create in the target model content
  • Dependent Rules : Optional part starting with 'then'. Specifies which - if any - rules a group or groups set of child rules to apply within the scope of the rule rule.

Rules in a group may be applied in any order; there is no sense of sequentially applying one rule after another. 7.6.0.7.1 Name Each rule is may be assigned a name. name, though this is usually inferred by the parser and not specified directly. The name is used when specifying rule links, and in traces trace logs (a record generated by the conversion engine recording the transform process). Names must be unique within the context of the map. Typically, the name is trivial and can be safely and usefully generated by the engine processing the map, so this is often left out.

The three main parts are described in more detail in the following sections.

Each rule specifies The source content is formed by one or more elements taken from the source that define variables that statements, which can be assigned a variable name and then be used when specifying target content, or re-used in subsequent transforms on the rule. and dependent rules. Multiple source elements statement are separated by a comma, like this: comma:

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

 [source], [source] -> ...

Each [source] contains the following items:

context.element { : type {min..max}} {default [value]} { list-option } as variable where [FHIRPath] check [FHIRPath]

For example:


  src.value : integer 0..* default 10 first as vs0
    where value >= 10 check value <= 100
    log value
  • context context.element : A variable that is The fist part of the source statement consists of two parts: the context in which this rule applies. Variables are not prefixed, and must be the element .

    The context is an identifier which is either declared elsewhere in as a source for the map ('as variable' below, or in the input as a source variables) parameter or any named variable within the group in which this rule is nested.

    The element : An optional is an (optional) name of a child element of the context. If this the name is not provided, the source is the context. If this it 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 "

  • type : A type may be specified. If a type is specified, only elements that have the specified type are selected as part of the source
  • min..max : Specified cardinality. cardinality (may use '*' for the upper bound). If a cardinality is specified, specified and the input document does cardinality of the element in the source is not conform to within the specified cardinality, the mapping engine raises an exception error instead of completing the transformation transformation.
  • default : A default value, used if there is no source value found. If there's a default value on an item that can repeat, it will only be used once once. Default values only apply to primitive types, and the value is a FHIRPath statement (usually a literal)
  • list-option : by default, the rule will apply once for each occurrence of the element in context. the source. The list option can override this to specify only to apply the first, last, or not to those different behaviour. Valid values are 'first', 'last', 'not_first', 'not_last' and 'only_one'
  • as variable : if an element is specified, a variable must be assigned. This variable name may be used in the target statement, and may be re-used in other dependent rules (see below). There is one special case described below (simple form) where the variable name is provided explicitly
  • where [condition] : a fluent path (ref) FHIRPath 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) FHIRPath expression that is evaluated to boolean on the context. If the expression returns false, the mapping engine terminates execution with raises an error. Note: it's it is 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 there are multiple source elements have a match statements, the rule applies for the permutation of the source elements (e.g. from each source statement. E.g. if there are 2 elements, source statements, each with 2 matches, matching elements, the rule applies 4 times, one for each combination). combination. Typically, if there is more than one source element, statement, only one of the elements can would repeat.

Once the source statement content is evaluated, the engine performing the evaluation has a list of variables, elements assigned to variables. For each time the rule is applied, each of which the variables contains a single value for each named variable. value. These variables are now mapped into the target structures in the target transformation.

Each source can include a log statement:


 log [expression]

Where expression is a FHIRPath statement. e.g.


 log 'not handled yet'

Puts a plain string in the log file. Alternatively, the log statement can contain FHIRPath:


 log src.field

Log statements are often used to note that some particular source element is not yet mapped.

Each rule specifies zero or more elements target transformation statements, which specify how source content is used to be created in the create target structure. content. These targets target statements can also be assigned to variables that can be used in subsequent transform rules. If no targets are specified, specified (no ->), no transformation is done and there are not no created targets, just newly defined source variables. variables, which can then be used in subsequent dependent rules. Multiple target elements statements are separated by a comma, like this:

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

... -> [target], [target] then...

Each [target] contains the following items:

make context.element = transform_code(parameters...) as variable {list_modes}

  context.element = transform_code(parameters...) as variable {list_modes}

For example:


  context.element = copy(parameter, ...) as vt1 first

  • context context.element : The name first part of a variable that is the target statement consists of two parts: the context in which and the content will be created. Variables must be element .

    The context is an identifier which is either declared elsewhere in as a target for the map ('as variable' below, map, a taret parameter or in any named variable within the input target variables) element : group (including the variables from the source content) in which this rule is nested.

    The element is the name of a child element that is valid in the context. The created value will be placed into the named element

  • transform+parameters transform(parameters) : details how the content that is created is transformed from the source data. data using a specific transformation function. See below for possible transform codes. the list of available functions. 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 the dependent rules
  • list_modes : control how elements that repeat are managed when the transform rule is evaluated evaluated. Values can be 'first', 'share', 'last' and 'collate'. TODO: What do these do?

Context and Element are optional as Transform statements may just contain an invocation of a pair. If no context/element is specified, then transform function. In this case, a variable must be defined, and the created value is only available in the variable. variable for use in subsequent transformations.

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

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

Name parameters Documentation
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 concrete 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
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 concrete syntax, this is simply represented as the source variable, e.g. src.a = tgt.b
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 FHIRPath expression and use the value returned by that. The 2nd parameter - FHIRPath expression - is evaluated in the context of the first parameter, and the result used as the value. In the concrete syntax, there is a short hand for this operation, by supplying () around the parameter. In this case, there is no context for the FHIRPath expression, and it must start with a reference to one of the defined variables
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 representation 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. detail. 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 specified 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 provided to the group that contains the rule, and those created by the application 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 are usually additional mapping rules that need to apply to the newly created variables.

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

 .. 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 nominate 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)

The parameters provided must match the parameters required by the dependenct dependent rule, in order. In addition, the mode of the variable must match - inputs that are targets must be target variables. Note, though, that target variables can be treated as source for a group.

Groups are resolved by name by looking through all the groups in all the available maps referened referenced by the uses (see above) statements. The name must be unique within the scope of these maps.

If no dependent rules are specified, and if the is only one source and target, and they both speecify specify a variable, the rule can be written in an abbreviated form:

 "name" : for src.element make tgt.element

 src.element -> tgt.element;

This is implicitly the same as

 "name" : for src.element as vvs make create([type]) tgt.element as vvt then [typeGroup](vvs, vvt)

  src.element as vvs -> tgt.element = create('type') as vvt then defaultMappingGroup(vvs, vvt)

Where the name of the type given as a parameter to 'create' and typeGroup the group invoked by the 'then' are determined by the context of src.element and tgt.element: tgt.element and the selected default mapping group, as documented in the next section. Note that default mapping groups are only invoked when no dependent rules or explicit group invocations are specified.

It is determined by looking through the available rule not necessary to explicitly invoke groups labeled for each mapping. Instead groups can be declared to be the "default" mapping for a given source and target type. Groups acting as "for types" defaults have either <<types>> or "for type+types" <<type+>> in their declaration.

Groups marked with types are used by default when the engine encounters a mapping with a source and checking target type where the types of their inputs looking for a match to the source and target type of src.element, the group. Of course, there can be only one such group for each combination of source and [type] target type for the engine to unambiguously determine which default group to invoke.

In addition to the above use, groups may be marked with type+ . They will act like a default mapping group, just like types , but additionally they will be invoked when the target type is determined by not fixed, i.e. when mapping to an element with a choice type. This means that a type+ group will be used as the default as long as the source type of tgt.element. If tgt.element can have more than one type, then the instance to map matches the source type is inferred by looking through of the available groups labeled "for type+types" for a match group. Even so, the target will then always be taken to be the target type of src.element the group.

todo The formal grammar for the mapping language, specified using ANTLR, can be found here .

Note that this grammar uses FHIRPath as an embedded syntax. Full details on FHIRPath and its grammar can be found here .

todo

This is the list of reserved keywords, which cannot be used as identifiers and names for variables, unless escaped.


map
uses
as
alias
imports
group
extends
default
where
check
log
then
true
false
types
type
first
not_first
last
not_last
only_one
share
collate
source
target
queried
produced
conceptMap
prefix