# Cross Version FML
FHIR Uses a special mapping language to define maps between FHIR versions.
* resource map
* simple property maps
* property renamed
* property moved into a backbone element
* property moved out of a backbone element
* backbone element map
* property type changed
## Understanding cross-version mapping
## resource map
The resource map has some metadata and a top level group.
The metadata is used to assist in tracking the map as a FHIR StructureMap resource, what structure definitions it requires (via `uses`), and other maps (via `imports`)
```
/// url = "http://hl7.org/fhir/uv/xver/StructureMap/Consent3to4"
/// name = "Consent3to4"
/// title = "Consent Transforms: R3 to R4"
/// status = "active"
uses "http://hl7.org/fhir/3.0/StructureDefinition/Consent" alias ConsentR3 as source
uses "http://hl7.org/fhir/4.0/StructureDefinition/Consent" alias Consent as target
imports "http://hl7.org/fhir/uv/xver/StructureMap/*3to4"
group Consent(source src : ConsentR3, target tgt : Consent) extends DomainResource <<type+>> {
src.identifier -> tgt.identifier;
...
}
...
```
There can be other groups in the file too.
Resource groups are the first in the file, and can be designated by the `extends DomainResource` marker on the group.
If the resource also has the `<<type+>>` indicator this map will be used in maps where a Resource type is encountered, such as in bundles, parameters and contained resources.
> **Note:** The canonical URL for the StructureDefinition has the FHIR Version defining format.
## Simple property maps
Where the property has been unchanged between the versions, then a simple copy map is used.
This is just source -> property
```
src.identifier -> tgt.identifier;
src.status -> tgt.status;
```
## Property renamed
This is the same as the simple property map, except use the new name.
```
src.consentingParty -> tgt.performer;
```
> **Note:** If the datatype
## property moved into a backbone element
This case saw `dose` and `rate` moved into a new backbone element called `doseAndRate`.
```
group Dosage(source src : DosageR3, target tgt : Dosage) {
src where (dose.exists() or rate.exists()) -> tgt.doseAndRate as vt0 then {
src.dose as vs -> vt0.dose as vt then Range(vs, vt) "doseRange";
src.rate as vs -> vt0.rate as vt then Ratio(vs, vt) "rateRatio";
} "doserate";
}
```
The source rule does not walk into any properties, just indicates that if there is a source, check for target rules and then call the dependent group.
Note the use of a fhirpath where clause to ensure that either of the properties exist before creating the backbone element. If there is a source value, then the target property/backbone element is created, and the dependendt rules are executed. Without the where clause, an empty doseAndRage could be created.
If there is only 1 property going into the backbone element, then this could be done in a single rule without the dependent rules.
## property moved out of a backbone element
```
group Dosage(source src : Dosage, target tgt : DosageR3) {
src where (dose.exists() or rate.exists()) -> tgt.doseAndRate as vt0 then {
src.dose as vs -> vt0.dose as vt then Range(vs, vt) "doseRange";
src.rate as vs -> vt0.rate as vt then Ratio(vs, vt) "rateRatio";
} "doserate";
src.doseAndRate as vs then {
vs.dose -> tgt.dose;
vs.rate -> tgt.rate;
}
}
```
> **Note:** When the engine(s) support the a.b.c identifier format, this could be shortened to the simple format `src.dateAndRate.dose -> tgt.dose`
## Mapping a BackboneElement
When mapping a backbone element, you can use either:
*(noting that you can also change the name at the same time as this too)*
### A dependent group
```
group Consent(source src : ConsentR3, target tgt : Consent) {
src.policy as vs -> tgt.policy as vt then ConsentPolicy(vs, vt);
}
group ConsentPolicy(source src, target tgt) extends BackboneElement {
src.authority -> tgt.authority;
src.uri -> tgt.uri;
...
}
```
### An inline dependent set of rules
This is equivalent to the preceding option, however you need to manually call the dependent group for the BackboneElement to bring any extensions through.
```
group Consent(source src : ConsentR3, target tgt : Consent) {
src.policy as vs -> tgt.policy as vt then {
vs.authority -> vt.authority;
vs.uri -> vt.uri;
...
vs -> BackboneElement(vs, vt);
};
}
```
## Property type changed
This specific example shows converting a code to a CodeableConcept
```
src.policyRule as v -> tgt.policyRule as cc,
cc.coding as c,
c.system = 'urn:ietf:rfc:3986',
c.code = v;
```
Here the target rules are chained where each one is using an alias (variable) preceding it.
Also note that there are no `create()` statements here, as the type of each property has only one valid type. just assigning the variable is enough to add it to the object.
In some cases such as a string to a `uri` or `canonical` there are existing maps that cover the type conversion, and in general are found in a map called `promitives.fml`, or alternative use an explicit conversion (that might also be in that file, such as `Identifier2Codeable`)
```
src.identifier as vs -> tgt.code as vt then Identifier2Codeable(vs, vt);
```
Alternatively you could use fhirpath to convert the type
```
// convert the quantity decimal amount to an integer
src.amount as sa -> tgt.amount = (sa.amount.ofType(Quantity).value.round().toString().toInteger()) "convertAmount";
```
### source/target choice datatype options are changed
Each of the datatypes available on the source property should be listed to indicate what should be done for each type.
If the list of types hasn't changed, then this splitting of types isn't needed.
```
src.rate : Ratio as vs -> vt0.rate = create('Ratio') as vt then Ratio(vs, vt) "rateRatio";
src.rate : Range as vs -> vt0.rate = create('Range') as vt then Range(vs, vt) "rateRange";
src.rate : Quantity as vs -> vt0.rate = create('Quantity') as vt then Quantity(vs, vt) "rateQuantity";
```
In this case note that a dependent group is used to create the Ratio/Range/Quantity, though these could also be done explicitly in-place as was done in the policyRule example above.
### Valueset binding changed
These could be implied from the source/target property bindings, and locate a conceptmap that applies for that combination.
However this can be explicitly declared too.
```
src.criticality as v -> tgt.criticality = translate(v, 'http://hl7.org/fhir/uv/xver/ConceptMap/ait.criticality-2to3', 'code') "AllergyIntolerance-criticality";
```
### Setting a primitive value with a fixed value
In some cases a simple hard coded value is required, usually these are based on some condition in the source detected via a where clause using fhirpath.
In the following example setting a code type property.
The engine will then perform type coersion to change the string to a code.
```
src.type as s where (s = 'choice') -> tgt.answerConstraint = 'optionsOnly';
```
## Backport extensions!
In order not to lose information, backport extensions can be used to retrieve values from an older version.
These may be used to hold:
* data for a field that did not exist in a previous (or latter) version
* data that has a different type that is not compatible with the target version
* coded data that is not supported in the target version's valueset definition
If the map processes an extension, this should be excluded from copying over to the target resource as an extension. This will mean you should not use the `extends DomainResource` or `extends BackboneElement` as these will blindly copy all the extension over.
**TBD:** Instead you should explicitly call the group and provide a parameter to indicate which will exclude the backport extensions.
``` fml
group ActivityDefinitionParticipant(source src, target tgt) {
src.type as v -> tgt.type
...
// The extension processing (including processing backport URLs)
src ->
('http://hl7.org/fhir/5.0/StructureDefinition/') as bpUrl,
// Regular extensions (and base)
BackboneElementXver(src, tgt, bpUrl),
// Backport extensions
tgt.propA = (src.extension(bpUrl & 'propA').value),
tgt.propB = (src.extension(bpUrl & 'propB').value)
"Extensions";
}
group BackboneElementXver(source src : BackboneElementR4, target tgt : BackboneElementR5, source extBackportUrlBase : string) {
// this excludes the cross version extensions
src.modifierExtension where (url.startsWith(extBackportUrlBase).not()) -> tgt.modifierExtension;
src -> ElementXver(src, tgt, extBackportUrlBase);
}
group ElementXver(source src : ElementR4, target tgt : ElementR5, source extBackportUrlBase : string) {
src.id -> tgt.id;
// this excludes the cross version extensions
src.extension where (url.startsWith(extBackportUrlBase).not()) -> tgt.extension;
}
```