This
page
is
part
of
the
FHIR
Specification
(v3.0.2:
STU
3).
(v3.3.0:
R4
Ballot
2).
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
Group
|
Maturity Level : 0 (Draft) | Ballot 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
ANTLR
grammar
for
the
concrete
syntax
).
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:
A map has 6 parts:
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:
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:
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
"//".
"//".
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), and must start with a letter. The special boolean 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]"map "[url]" = "[name]"
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.
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]" (alias name) as [mode] // documentationuses "[url]" (alias name) 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. It may also provide an 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).
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:
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 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 section references additional maps that are used by this map:
imports "[url]" // documentationimports "[url]" // documentation
Typically, maps that are imported are 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.
The
[url]
may
contain
a
"*"
"*"
as
a
wildcard
character
to
include
any
matching
maps
that
are
available
to
the
mapping
engine.
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 (for type) [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 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.
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 variables also have a mode, which may be one of source or target (see above). There must be at least two input variables (source and target) - else there's nothing to map. Maps 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
name,
type
and
mode)
though
their
order
may
differ,
and
it
MAY
have
additional
parameters.
The
key
word
for
is
used
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
the
target
is
not
fixed,
use
this
group
In both these cases, the group SHALL have 2 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].
Each rule has 4 main sections:
Rules in a group may be applied in any order; there is no sense of sequentially applying one rule after another.
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 transforms on the rule. Multiple source elements are separated by a comma, like this:
rule_name: for [source], [source], make ...
Each [source] contains the following items:
context.element { : type {min..max}} {default [value]} { list-option } as variable where [FHIRPath] check [FHIRPath]
If all the source elements have a match 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 list of variables, each of which contains a single value for each named variable. 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 and Element are optional as a pair. If no context/element is specified, then a variable must be defined, and the created value is only available in the variable.
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:
| 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 |
| 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
|
| 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
|
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 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
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"name" : for src.element make 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)"name" : for src.element as vvs make create([type]) tgt.element as vvt then [typeGroup](vvs, vvt)
Where
type
and
typeGroup
are
determined
by
the
context
of
src.element
and
tgt.element:
typeGroup
is
determined
by
looking
through
the
available
rule
groups
labeled
as
type
is
determined
by
the
type
of
tgt.element.
If
tgt.element
can
have
more
than
one
type,
then
the
type
is
inferred
by
looking
through
the
available
groups
labeled
todo
todo