Maturity Level: 3 | Standards Status:Trial Use |
Source | ISO 3166 Home Page |
System |
ISO 3166 is a 3 part standard, each of which defines a code system:
|
Version | If necessary to exchange the version (see below), use the year of release in the format YYYY |
Code |
|
Display | Use the Country or State/Province Name |
Inactive | There are no codes that are inactive |
Subsumption | No Subsumption relationships are defined amongst the Country Codes |
Filter Properties | Regex on code - see below |
A few country codes have been reused (e.g. CS). If a version is needed, simply use the year of publication e.g. 1998.
ISO Maintains the copyright on the country codes, and controls it's use carefully. For further details, see the ISO 3166 Home Page
The Part 1 Code system identified by the URI urn:iso:std:iso:3166
contains
3 types of codes for country:
In almost all contexts of use, only one of these 3 subsets of codes are used / allowed. Value sets can be defined for these by enumerating the relevant codes. Alternatively, a regex can be used to define the value set intensionally. For example, this filter specifies only 3 letter codes:
"filter" : [{ "property" : "code", "op" : "regex", "value" : "[A-Z]{3}" }]
This specification defines 3 commonly used value sets for use with ISO 3166 Country Codes:
The latter two value sets are defined intensionally in this specification as a consequence of ISO Intellectual Property Restrictions.
Maturity Level: Normative | Standards Status:Normative |
The JSON representation for a resource is based on the JSON format described in STD 90 (RFC 8259), and is described using this format:
{ "resourceType" : "[Resource Type]", // from Source: property0 "property1" : "<[primitive]>", // short description "property2" : { [Data Type] }, // short description "property3" : { // Short Description "propertyA" : { CodeableConcept }, // Short Description (Example) }, "property4" : [{ // Short Description "propertyB" : { Reference(ResourceType) } // R! Short Description }] }
Using this format:
property1
has a primitive data type; the value of the property will be as described for the stated typeproperty2
has a complex data type; the value of the property is an object that has the content as described for the stated typeproperty3
is an object property that contains additional properties (e.g. propertyA; the allowable properties are listed (but also include extensions as appropriate)property4
is an array property that contains items which are objects themselves. The items may have any of the types already encountered in points 1-3propertyA
is an example of an object property that has a binding to a value set - the Short description is a link to the value set. In addition, the binding strength is shown propertyB
is an example of an object property that has a reference to a particular kind of resource //
is used for comments. While // is legal in Javascript, it is not legal in JSON, and comments SHALL not be in JSON instances irrespective of whether partiular applications ignore themapplication/fhir+json
.Given the way extensions work, applications reading JSON resources will never encounter unknown properties. However, once an application starts trading with other applications that conform to later versions of this specification, unknown properties may be encountered. Applications MAY choose to ignore unknown properties in order to foster forwards compatibility in this regard, but may also choose not to.
The JSON format is similar to the XML format:
There are differences from XML:
resourceType
id
, value
) are handled differently (see below)<div>
element in the Narrative datatype is represented as a single escaped string of XHTML. This is to avoid problems in JSON with mixed content, etc. The XHTML SHALL still conform to the rules described for the NarrativeThe JSON format for the resources follows the standard XML format closely to make interconversion easy, and so that XPath queries can easily be mapped to query the JSON structures. However, the differences - particularly the repeating element one, which cannot be avoided - mean that generic XML --> JSON converters are not able to perform correctly. The reference platforms provide XML <--> JSON conversion functionality that accommodates these FHIR-specific characteristics.
An element that has a maximum cardinality of >1 (e.g. x..*
in the definitions) may occur more than once in the instance.
In XML, this is simply done by repeating the XML element multiple times. In JSON, this is
done by using an array type. Note that:
<code> <coding> <system value="http://snomed.info/sct"/> <code value="104934005"/> </coding> <coding> <system value="http://loinc.org"/> <code value="2947-0"/> </coding> </code>
is represented in JSON like this:
{ "coding": [ { "system" : "http://snomed.info/sct", "code" : "104934005" }, { "system" : "http://loinc.org", "code" : "2947-0" } ] }
FHIR elements with primitive data types are represented in two parts:
_
prepended to the name of the element, which, if present, contains the value's id and/or extensionsThe FHIR types integer and decimal are represented as a JSON number, the FHIR type boolean as a JSON boolean, and all other types are represented as a JSON string which has the same content as that specified for the relevant data type. Whitespace is always significant (i.e. no leading and trailing spaces for non-strings).
<code value="abc"/> <!-- code --> <date value="1972-11-30"/> <!-- dateTime --> <deceased value="false" /> <!-- boolean --> <count value="23" /> <!-- integer -->
is represented in JSON as
"code" : "abc", "date" : "1972-11-30", "deceased" : false, "count" : 23
When using a JavaScript JSON.parse() implementation, note that JavaScript natively supports only one numeric datatype, which is a floating point number. This can cause loss of precision for FHIR numbers. In particular, trailing 0s after a decimal point will be lost e.g. 2.00 will be converted to 2. The FHIR decimal data type is defined such that precision, including trailing zeros, is preserved for presentation purposes, and this is widely regarded as critical for correct presentation of clinical measurements. Implementations should consider using a custom parser and big number library (e.g. https://github.com/jtobey/javascript-bignum) to meet these requirements.
If the value has an id attribute, or extensions, then this is represented as follows:
<birthDate id="314159" value="1970-03-30" > <extension url="http://example.org/fhir/StructureDefinition/text"> <valueString value="Easter 1970"/> </extension> </birthDate>
is represented in JSON as:
"birthDate": "1970-03-30", "_birthDate": { "id": "314159", "extension" : [ { "url" : "http://example.org/fhir/StructureDefinition/text", "valueString" : "Easter 1970" }] }
Note: If the primitive has an id attribute or extension, but no value, only the property with the _
is rendered.
In the case where the primitive element may repeat, it is represented in two arrays. JSON null values are used to fill out both arrays so that the id and/or extension are aligned with the matching value in the first array, as demonstrated in this example:
<code value="au"/> <code value="nz"> <extension url="http://hl7.org/fhir/StructureDefinition/display"> <valueString value="New Zealand a.k.a Kiwiland"/> </extension> </code>
is represented in JSON as:
"code": [ "au", "nz" ], "_code": [ null, { "extension" : [ { "url" : "http://hl7.org/fhir/StructureDefinition/display", "valueString" : "New Zealand a.k.a Kiwiland" }] } ]
Note: when one of the repeating elements has no value, it is represented in the first array using a null. When an element has a value but no extension/id, the second array will have a null at the position of that element.
The representation of primitive data types has been split into two parts like this in order to simplify the representation of simple primitive values without id or extensions. This does have the cost of making the representation of the id attribute and extensions more ungainly, but these are both rarely used with primitive data types.Elements, and complex datatypes (types that contain named elements of other types) are represented using a JSON object, containing a member for each element in the datatype. Composites can have id attributes, which are converted to JSON member values, in the same manner as described for primitives. For example:
<Patient> <text> <status value="generated" /> <div xmlns="http://www.w3.org/1999/xhtml"><p>...</p></div> </text> <name id="f2"> <use value="official" /> <given value="Karen" /> <family id="a2" value="Van" /> </name> </Patient>
is represented in JSON as:
{ "name" : [{ "id" : "f2", "use" : "official" , "given" : [ "Karen" ], "family" : "Van", "_family" : {"id" : "a2"} }], "text" : { "status" : "generated" , "div" : "<div xmlns=\"http://www.w3.org/1999/xhtml\"><p>...</p></div>" } }
Things to note here are:
name
, the id
is added represented in _family
as described above, while the id
on the name itself is represented as just another propertydiv
element which is in the Narrative element text
is represented
as an escaped string in the value property in JSON. The xhtml root element needs to be a <div> in the xhtml namespace
A resource is a JSON object with a property resourceType
which informs the
parser which resource type this is:
{ "resourceType" : "Patient", "text" : { "status" : "generated" , "div" : "<div xmlns=\"http://www.w3.org/1999/xhtml\"><p>...</p></div>" } // etc... }
Note that parsers cannot assume that the resourceType property will come first.
This is a problem for several JSON -> Object serializers that assume that the resourceType property does come first, including Json.NET. However, some JSON generators do not give the authoring application control of the order of the property values, and so these implementations cannot interoperate with implementations that make assumptions about order. Given that JSON says that the property values are an unordered map of name/value pairs, this specification cannot require that properties come in any particular order, though implementers may choose to fix the property order if they are able (and the reference platforms provided with this specification do so).There is a sample file with many edge cases to help test JSON parsers.
Resources and/or Bundles may be digitally signed (see Bundle and Provenance).
This specification defines the following method for canonicalizing FHIR resources, when represented as JSON:
This canonicalization method is identified by the URI http://hl7.org/fhir/canonicalization/json
. The
following additional canonicalization URIs are also defined:
http://hl7.org/fhir/canonicalization/json#data | The narrative (Resource.text ) is omitted prior to signing (note the deletion is at Resource.text , not Resource.text.div ) |
http://hl7.org/fhir/canonicalization/json#static | In addition to narrative (Resource.text), the Resource.meta element is removed. This makes the signature robust as the content is moved from server to server, or workflow and access tags are added or removed. Note that workflow and security tags may contain information important to the handling of the resource, so meta elements should be protected from tampering by other means if unsigned. |
http://hl7.org/fhir/canonicalization/json#narrative | This method only retains the Resource.id and Narrative elements |
http://hl7.org/fhir/canonicalization/json#document | The signs everything in a Bundle, except for the Bundle.id and Bundle.metadata on the root Bundle (allows for a document to be copied from server to server) |
These canonicalization methods allow system the flexibility to sign the various portions of the resource that matter for the workflow the signature serves. These canonicalization algorithms do not work for enveloped signatures. This will be researched and addressed in a future release. This specification may define additional canonicalizations in the future, and other specifications might also define additional canonicalization methods.
One consequence of signing the document is that URLs, identifiers and internal references are frozen and cannot be changed. This might be a desired feature, but it may also cripple interoperability between closed ecosystems where re-identification frequently occurs. For this reason, it is recommended that systems consider carefully the impact of any signature processes. The impact of signatures on Document bundles and their related processes is the most well understood use of digital signatures.Maturity Level: n/a | Standards Status:Informative |
In general, with regard to human language, the content in resources falls into one of three categories
The vast majority of clinical content is language neutral - the language is either unknown or unspecified, and assumed to be in the natural language predominant in the implementation environment. Note that this doesn't mean that the content is all in the same language - often, snippets of other language content will creep in where patients or providers themselves are multi-lingual, but there is no formal tracking for such content, and it will often be translated in situ for the benefit of all users.
While most applications are entirely language neutral, many healthcare applications must deal with multi-lingual content. This page provides a roadmap to the facilities in the FHIR specification for supporting multiple languages.
FHIR offers the following facilities for dealing with languages:
At this time, there is no way to provide multiple translations for the resource narrative itself. See the open issue for further details.
The specification itself is published and balloted in English (US). There are projects to translate this to other languages (e.g. Russian and Japanese), but these are generally not well maintained.
One consequence of the way the specification works is that the element names (in either JSON or XML) are in English. These should be considered as references into the specification; they’re never meant to be meaningful to any one other than a user of the specification itself, and are never intended to be displayed to end-users of any healthcare application.
What may be displayed to users are the display terms for codes and elements, which can be managed as described below. This has been done for some codes/elements and languages (e.g. see Common Languages), and further contributions from affiliates or implementers is welcome.
Every resource has an optional language
element that is the base language for the content of the resource. Note that this does not
require that all the content of the resource be in the specified language; just that unless
otherwise specified, this is the default language of the content.
In addition, an xml:lang
attribute can be specified on the html:div
element
(or any element in the HTML). This can be used to specify a language in addition to the resource
language. Note that it doesn't really make sense to specify a different language on the narrative
than on the resource itself; the xml:lang attribute is allowed to support natural language processing
of the narrative.
Where resources contain the same documentation in several different languages, each
different languages should use a div
element with an XHTML lang
attribute in the root div. By default, all languages will be displayed to a user,
but multi-language aware applications can filter the content by language and only display the
language that ia relevant to or chosend by the patient.
See the W3C documentation on Language declarations for further information.
All human readable character content (any elements of type string and markdown) has the same language as that specified by the resource language element. Whether or not a language is specified, translations may be provided for any human readable character content. Translations are provided using the translation extension:
{ "url" : "http://hl7.org/fhir/StructureDefinition/translation", "extension" : [{ "url" : "lang", "valueCode" : "de" },{ "url" : "content", "valueString" : "einige übersetzte Wörter" }] }
Multiple translations can be provided if more than one language is required.
Concepts defined in CodeSystem resources can have one or more designations that provide additional representations for the concept (CodeSystem.concept.designation):
{ "code": "de", "display": "German", "designation": [{ "language": "nl", "value": "Duits" },{ "language": "ru", "value": "Немецкий" }, { "language": "zh", "use": { "system": "http://terminology.hl7.org/CodeSystem/designation-usage", "code": "display" }, "value": "德语" } ] }
The base display ("German" in this case) is in the language specified on the resource. Applications using the code system can pick the appropriate language based on the context and/or relevant parameters when using the code system. Alternate designations can also be specified when concepts are include in value sets (ValueSet.compose.include.concept.designation).
All elements in FHIR resources or data types are defined by an ElementDefinition. Element Definitions provide several different descriptions for each element, the most important of which are label and definition. These (and other string and markdown properties on the element definition can have translations using the extension shown above.
In addition to this, elements can be marked with the translatable extension:
{ "url" : "http://hl7.org/fhir/StructureDefinition/elementdefinition-translatable", "valueBoolean" : true }
This indicates that applications should consider supporting translations when reading/writing this element.
HTTP Requests can include a header to indicate which language is requested:
Accept-Language: de-DE
The header can contain multiple languages, with weighted preferences. Servers can use the header to return the correct language if they are able. If the request is a search, the server may choose to limit matches to those that contain the correct language, though this might be unsafe.
HTTP Responses can include a header to indicate which language is in the resource:
Content-Language: en-US
If present, the content language header must match the language of the resource (if specified).
Names and addresses are one area of special difficulty with regard to multiple languages. Inherently, names and addresses only have one language, but they may have approximations in other languages, or people may choose to take entirely different names for different cultures. Some of the name examples illustrate how this is handled.
One common practice in some cultures is the represent the same name twice, in local and western scripts. These can be differentiated by the script portion of the language extension:
<name> <extension url="http://hl7.org/fhir/StructureDefinition/language"> <valueCode value="ru-RU-Cyrl"/> </extension> <family value="ЕМЕЛИН" /> <given value="ИВАН" /> <given value="ВЛАДИМИРОВИЧ" /> </name> <name> <extension url="http://hl7.org/fhir/StructureDefinition/language"> <valueCode value="ru-RU-Latn"/> </extension> <family value="EMELIN" /> <given value="IVAN" /> <given value="VLADIMIROVICH" /> </name>
The different scripts can also be differentiated by the Unicode subsets that they contain, but this is not always convenient.
Maturity Level: N/A | Standards Status:Informative |
FHIR Resource definitions developed by HL7 are derived from the considerable collective experience of the HL7 membership and wide community feedback from the development and application of a spectrum of health care interoperability solutions. However, Resource definitions are generalized to support multiple contexts of use. It is the responsibility of the persons or organizations using these Resources to ensure their use is fit for the particular purpose in which they are used, including validation for clinical and operational use.
See also the specific warnings associated with use of the STU.
Copyright © 2011+ HL7.
This specification (specifically the set of materials included in the fhir-spec.zip file available from the Downloads page of this specification) is produced by HL7 under the terms of HL7® Governance and Operations Manual relating to Intellectual Property (Section 16), specifically its copyright, trademark and patent provisions.
This document is licensed under Creative Commons "No Rights Reserved" (CC0).
HL7®, HEALTH LEVEL SEVEN®, FHIR® and the FHIR ® are trademarks owned by Health Level Seven International, registered with the United States Patent and Trademark Office.
While HL7 places no restrictions on the right to copy the FHIR specification or to create derivative works per the Creative Commons license, HL7 protects the "FHIR" trademark carefully. This means that:
Trademark FAQs are posted on the HL7 International website. Questions? Please contact HL7trademarks@HL7.org for more information.
The HL7 FHIR specification contains and references intellectual property owned by third parties ("Third Party IP"). Acceptance of these License Terms does not grant any rights with respect to Third Party IP. The licensee alone is responsible for identifying and obtaining any necessary licenses or authorizations to utilize Third Party IP in connection with the specification or otherwise.
Following is a non-exhaustive list of third-party artifacts and terminologies that may require a separate license:
Artifact/Terminology | Statement/Owner/Contact |
SNOMED CT | International Healthcare Terminology Standards Developing Organization (IHTSDO). This specification includes content from SNOMED CT, which is copyright © 2002+ International Health Terminology Standards Development Organisation (IHTSDO) and distributed by agreement between IHTSDO and HL7. Implementer use of SNOMED CT is not covered by this agreement |
DICOM | National Electrical Manufacturers Association (NEMA). This specification includes content from DICOM, which is copyright NEMA, and distributed by agreement between NEMA/DICOM and HL7. Implementer use of DICOM is not covered by this agreement |
Logical Observation Identifiers Names & Codes (LOINC) | This material contains content from LOINC® (http://loinc.org). The LOINC table, LOINC codes, and LOINC panels and forms file are copyright © 1995-2014, Regenstrief Institute, Inc. and the Logical Observation Identifiers Names and Codes (LOINC) Committee and available at no cost under the license at http://loinc.org/terms-of-use. |
International Classification of Diseases (ICD) codes | Consult the World Health Organization (WHO) |
Current Procedures Terminology (CPT) code set | American Medical Association (AMA) CPT copyright 2014 American Medical Association. All rights reserved. |
Maturity Level: 3 | Standards Status:Trial Use |
This page describes several issues around lifecycle management for the resources and the content they contain. Specifically, this page describes:
Many FHIR resources have a status element that represents the lifecycle state of the resource or the clinical process represented by the resource. Work groups can specify status values appropriate to the individual resource. Although consistency between resources is not the primary objective, it is helpful to users and developers to have well-crafted value sets that cover all possible states (since the value sets are typically required and non-extensible).
To understand existing status elements, and to help create extensions and resources involving resource states, we note that status value sets follow one of the following life cycles:
For additional information about managing resource life cycles, see:
Describes the lifecycle states of complex activities common in healthcare. Typically, these states follow a chronological life cycle that leads from initiation to the conclusion of the action. A characteristic (but non-exhaustive) set of states for the clinical workflow process life cycle include:
Examples of the clinical workflow life cycle:
Some resources in FHIR represent orders or requests. The request lifecycle can be generalized in terms of four stages: creating the request, sending the request, receiving acceptance or refusal of the request, and fulfillment of the request. A characteristic (but non-exhaustive) set of states for the request/order pattern include:
Examples of the request/order life cycle:
The entity availability life cycle indicates if the resource, or the entity described by the resource, is ready for use, not yet ready for use, or has been retired from use. A characteristic (but non-exhaustive) set of states for the entity availability life cycle include:
Examples of the entity availability life cycle:
Clinical status is somewhat different than the previous status values, since it does not deal with workflow or lifecycle. Instead, it indicates how evidence is affecting a clinical interpretation. Here are two examples:
Many clinical systems maintain current lists of some kind of resources for a patient. Some of the commonly maintained lists include:
Because of the way that resources are used, there is no simple way to determine, from examination of a resource, whether it is 'current' or not. Take, as an example, the Condition resource. In a typical EHR, condition resources might be published on the RESTful interface for the following reasons:
There is no element on the Condition resource that can convey the difference between these usages. In particular, there can be no way to differentiate between current and past resources without having to retrospectively alter resources, which is problematic with regard to integrity and digital signatures.
One consequence of this is that searching the condition resource for a given patient will return more than just the patient's current problems. Though this is somewhat counter-intuitive to some implementers, restricting searches on Condition to only include the patient's current curated problem list excludes all the other - important - uses of the Condition resource.
Determining whether a Condition is an entry on a patient's current problem list is done by checking with the Condition resource is referenced from the correct list.
On the RESTful API, this is done using the list search mechanism:
GET [base]/AllergyIntolerance?patient=42&_list=$current-allergies
This is a request to fetch all the allergies in the patient 42's "Current Problem List". Note that the server is not required to actually make a resource representation of the current allergy list available, though doing so assists clients in their audit/integrity tasks. See List Operation "Find" for further information.
In a document, current lists are determined by the code on a Composition section.
FHIR defines the following names for functional lists:
List | ResourceType | Description | Possible LOINC codes in documents / sections |
$current-problems | Condition | The "Current Problem List" - A list of current and active diagnoses as well as past diagnoses relevant to the current care of the patient | 46105-3 (Problem conditions Set) |
$current-medications | MedicationStatement/ MedicationRequest | A list of all medications that the patient is taking. The 'current medications list' sometimes may include a mix of prescribed and over-the-counter medications - or only some of them. The list may contain a mix of prescriptions and more general statements, or only one of the two. The list may also correspond to a formal reconciled medication administration schedule, but more often does not | 57828-6 (Prescription list), 10160-0 (History of medication) |
$current-allergies | AllergyIntolerance | A list of known or suspected propensities to medications, foods, or environmental agents that is provided to help prevent reactions while care is occurring | 18716-1 (Allergy studies (set)), 52472-8 (Allergies and Adverse Drug Reactions), and 48765-2 (Allergies and adverse reactions Document) |
$current-drug-allergies | AllergyIntolerance | A list of known or suspected propensities to medications that is provided to help prevent reactions while care is occurring. This list is a subset of the full allergies list | (same as above?) |
The entered-in-error state indicates the resource was created accidentally and should be ignored. This state can apply to resources created by manual entry. It is usually not associated with the Clinical Workflow Process life cycle, but can be associated with the Request/Order and the Entity Availability life cycles.
This table summarizes what is expected to happen for each resource in the case that the data it contains is subsequently found to be an erroneous entry.
Note: Resources that are not listed in this table do not have any explicit documentation with regard to being entered in error.
Maturity Level: N/A | Standards Status:Informative |
A logical model is an expression of a set of content in a FHIR ready style that helps implementers understand the content in a single package. Because of the way FHIR works, a single logical model may be actually represented during exchange by a set of resources, including a series of observations and questionnaires. Typically, profiles that define how the resources are used are derived from the logical model.
ToDo: figure out how all this happens.
Maturity Level: 5 | Standards Status:Trial Use |
Source | LOINC is made available by the Regenstrief Institute at http://loinc.org |
System | The URI http://loinc.org identifies LOINC codes |
Version | Where a version is used, it should be the standard LOINC version e.g. 2.48 |
Code | The LOINC Code Identifier e.g. 21176-3. LOINC codes are not case sensitive. In addition, LOINC part codes and LOINC answer string ids can be used where appropriate |
Display | Use either the SHORTNAME or LONG_COMMON_NAME field for the display |
Inactive | codes with Property ACTIVE=DEPRECATED are considered inactive |
Subsumption | LOINC defines the Multi-Axial Hierarchy, which is the basis for subsumption logic in LOINC |
Filter Properties | Several properties are defined as described below |
The terms of use for LOINC require that a notice be included with any use of LOINC codes. This notice must appear in the copyright element of any value set that includes LOINC codes (either in the code or filter elements, or in an expansion):
<copyright value="This content LOINC® is copyright © 1995 Regenstrief Institute, Inc. and the LOINC Committee, and available at no cost under the license at http://loinc.org/terms-of-use"/>
Additional copyright statements may also be found in the copyright element. Some LOINC codes have 3rd party copyright statements. When these codes are included in a value set, they must carry their own copyright statement as well.
For comparison purposes, LOINC codes, displays, and property values are not case sensitive, though implementers SHOULD maintain the correct case when using LOINC codes and property values.
As described in the LOINC Manual section 10.2, LOINC Parts are a coded representation of a value for a dimension used to specify a LOINC Term which are assigned a non-semantic identifier with a "LP" prefix and a mod-10 check digit. Following the LOINC license, these part codes may be used in the following ways:
Part codes are the same LOINC system (http://loinc.org) and SHALL be represented in uppercase (e.g. LP31755-9).
LOINC also allocates Answer List and Answer String Ids for use in various forms and questionnaires. LOINC Answer String IDs are also valid LOINC codes:
<coding> <system value="http://loinc.org"/> <code value="LA11165-0"/> <display value="Platelet anisocytosis"/> </coding>
LOINC Answer List Ids are actually value set identifiers. See below for how to use these.
LOINC uses the namespace http://loinc.org/rdf# as the root for LOINC concepts in the RDF space. This means that when a LOINC code is converted from the system:code pair, where the system is http://loinc.org, to the RDF ontological form, the representation is http://loinc.org/rdf#[code].
In addition to the standard properties, the following properties are defined for LOINC:
STATUS | boolean | Status of the term. For the purposes of LOINC, codes with Property ACTIVE=DEPRECATED are considered inactive |
COMPONENT | code | First major axis-component or analyte: Analyte Name, Analyte sub-class, Challenge |
PROPERTY | code | Second major axis-property observed: Kind of Property (also called kind of quantity) |
TIME_ASPCT | code | Third major axis-timing of the measurement: Time Aspect (Point or moment in time vs. time interval) |
SYSTEM | code | Fourth major axis-type of specimen or system: System (Sample) Type |
SCALE_TYP | code | Fifth major axis-scale of measurement: Type of Scale |
METHOD_TYP | code | Sixth major axis-method of measurement: Type of Method |
CLASS | string | An arbitrary classification of the terms for grouping related observations together |
CONSUMER_NAME | string | An experimental (beta) consumer friendly name for this item. The intent is to provide a test name that health care consumers will recognize; it will be similar to the names that might appear on a lab report |
CLASSTYPE | string | 1=Laboratory class; 2=Clinical class; 3=Claims attachments; 4=Surveys |
ORDER_OBS | string | Provides users with an idea of the intended use of the term by categorizing it as an order only, observation only, or both |
DOCUMENT_SECTION | string | Classification of whether this LOINC code can be used a full document, a section of a document, or both |
Note that when a $lookup operation is performed on a LOINC code,
servers SHALL return the version being used (see above) in the version
property.
Other properties are at the discretion of the server and the client.
This section documents the property filters that can be used with the LOINC code system in value set composition statements.
Description | Allows the selection of a set of LOINC codes with a common property value (see list above) |
Property Name | One of the names listed in the "Field Name" column in LOINC Database Structure (Appendix A of the LOINC manual) |
Operations Allowed | = / regex |
Values Allowed | [string value] |
Comments | The 6 properties COMPONENT, PROPERTY, TIME_ASPCT, SYSTEM, SCALE_TYP, and METHOD_TYP are most likely to be useful. |
Description | Allows for the inclusion or exclusion of LOINC codes that include 3rd party copyright notices |
Property Name | copyright |
Operations Allowed | = |
Values Allowed | LOINC | 3rdParty |
Comments | LOINC = only codes with a sole copyright by Regenstrief. 3rdParty = only codes with a 3rd party copyright in addition to the one from Regenstrief. |
Description | Allows for the selection of a set of codes base on their appearance in the LOINC multi-axial hierarchy |
Property Name | parent | ancestor |
Operations Allowed | = / in |
Values Allowed | Part Code (or, for "in", multiple part codes separated by commas) |
Comments | "parent" selects immediate parents only. For example, the code "44022-2" has the parent "LP52960-9". Ancestor includes parents transitively, e.g. "LP52960-9" eventually has a parent "LP31755-9", so the code "44022-2" is in the set of codes that have ancestor=LP31755-9 |
TODO: Document Ontology
Implicit value sets are those whose specification can be predicted based on the grammar of the underlying code system, and the known structure of the URL that refers to them. LOINC defines one set of implicit value sets: By Multi-Axial Hierarchy Entry.
If any value set resources exist with an identifier that conforms to the URL patterns specified below, the content of the resource must conform to the template provided. Profiles and other value set references are allowed to reference these value sets directly. todo: can LOINC actually host a service that returns these?
The value set identifier http://loinc.org/vs is a value set that contains all LOINC codes.
LOINC defines a set of Answer lists, each of which contains a set of LOINC codes. LOINC answer lists are value sets. The value set identifier "http://loinc.org/vs/[id]" identifies a value set that contains a set of LOINC codes. For instance, the value set identifier http://loinc.org/vs/LL715-4 has the following definition for LOINC 2.52:
<ValueSet xmlns="http://hl7.org/fhir"> <text> <status value="generated"/> <div xmlns="http://www.w3.org/1999/xhtml"> [some html that identifies that this value set includes all LOINC codes in this answer list] </div> </text> <url value="http://loinc.org/vs/LL715-4"/> <version value="2.52"/> <name value="Platelet morph"/> <description value="LOINC Answer List for Platelet morph"/> <status value="active"/> <date value="[optional date of LOINC release]"/> <compose> <include> <system value="http://loinc.org"/> <concept> <code value="LA11165-0"/> <display value="Platelet anisocytosis"/> </concept> <concept> <code value="LA11168-4"/> <display value="Platelet clump"/> </concept> <concept> <code value="LA11167-6"/> <display value="Platelet large fragments"/> </concept> <concept> <code value="LA11166-8"/> <display value="Platelet satellitism"/> </concept> <concept> <code value="LA11169-2"/> <display value="Platelets.agranular"/> </concept> <concept> <code value="LA11170-0"/> <display value="Platelets.giant"/> </concept> <concept> <code value="LA11172-6"/> <display value="Platelets.large"/> </concept> <concept> <code value="LA11171-8"/> <display value="Platelets.small"/> </concept> </include> </compose> </ValueSet>
Here is an example of a LOINC Answer list used in a Questionnaire question:
<question> <concept> <system value="http://loinc.org"/> <code value="11125-2"/> <display value="Plat morph Bld"/> </concept> <type value="choice"/> <options> <reference value="http://loinc.org/vs/LL715-4"/> </options> </question>
A value set with an identifier of "http://loinc.org/vs/[partcode]" must conform to this template, where [partcode] is a part code from the multi-axial hierarchy:
<ValueSet xmlns="http://hl7.org/fhir"> <text> <status value="generated"/> <div xmlns="http://www.w3.org/1999/xhtml"> [some html that identifies that this value set includes all LOINC codes subsumed by the identified Multi-Axial Hierarchy Part Code] </div> </text> <url value="http://loinc.org/vs/[partcode]"/> <version value="[optional - but strongly recommended - LOINC version]"/> <name value="LOINC Value Set from Multi-Axial Hierarchy code [partcode]"/> <description value="All LOINC codes for [partcode or name]"/> <status value="active"/> <date value="[optional date of LOINC release]"/> <compose> <include> <system value="http://loinc.org"/> <filter> <property value="ancestor"/> <op value="="/> <value value="[partcode]"/> </filter> </include> </compose> </ValueSet>
The appropriate type is determined by mapping from the LOINC specified SCALE_TYP for v3 to FHIR using this table:
SCALE_TYP | FHIR data type |
Qn | Quantity |
Ord | CodeableConcept |
OrdQn | Quantity and CodeableConcept (e.g. both in the data element; either is allowed) |
Nom | CodeableConcept (and string?) |
Nar | markdown (?) |
Multi | Attachment |
Doc | Attachment |
Maturity Level: n/a | Standards Status:Informative |
The content on this page builds on the Normative documentation about managing resource identification.
Each resource has a known identity, which is a URI. All resources accessed through the RESTful API have a URL for their identity. The identity is not stored inside the resource, but must be tracked by systems handling resources. For RESTful systems, the resource identity is the same as the URL by which it is found. When a resource is packaged in a bundle (e.g. for messages, documents, or exchanged in a service), the id is included along with the resource. When systems using FHIR resources are deployed in an implementation context where there are more than two systems exchanging resources, the way that resources are identified with URLs needs to be managed carefully.
Note that many resources also have an identifier
element, and usually there can be
multiple identifiers. This identifier is a logical identifier for the concept that
the resource describes and is different from the identity discussed in this section,
which is the literal identity of the resource. Note that there are often multiple
resource instances that describe the same concept across multiple systems, e.g. each
application keeps its own copy of the patient information in a
Patient resource. This can even exist within a single
system, such as in the case of patient duplicates.
Some resources have a special identifier, which is constrained to be a proper URI, and which is known as the 'canonical URL' for the resource. These resources are referred to by this URI, which is encouraged to be an actual URL that resolves to a web page (or web service) that describes the content (hence 'canonical URL' not 'canonical URI', though this is also for legacy purposes).
Resources are used in a variety of circumstances. Generally, these can be categorized into 3 different scenarios:
These combinations are why either relative (logical) or absolute references are allowed, and why a logical id is always required, in order to enable seamless exchange amongst partially closed trading systems.
When resources are exchanged between systems, they may need to have new logical resource ids assigned. When a resource has a new logical id assigned, nothing in the resource changes other than the Resource.id, but any references that point to the resource need to be updated. Whether new ids are required or not depends on the context, as does how resource references are updated.
The normal case is that a client/receiving system accepts the server/sender's identification of a resource at face value, whether it is a relative or absolute reference. When the client/receiver wants to follow resource references, they are done using the server id (typically either by http calls or locating them in a bundle), but other arrangements are possible. In such cases, there is no need for new ids.
Another scenario is for a client to retrieve a resource from a server and make its own local persistent copy. If the local resource has a life-cycle of its own (i.e. it is not just a cached resource), then it needs to have its own identity; i.e. the resource must get new ids. The simplest case is that the client is only keeping local copies of resources from a single server. In these cases, the client can simply replace the Base URL and keep the logical id of the resource the same. In fact, if the server is using relative references, then this change doesn't involve any actual changes to the resources, it only means a re-interpretation of the references.
In some cases, however, the client may deal with multiple servers. In this case, the logical id of the resource is not guaranteed to be unique (unless all resources have a UUID for the logical id, which is allowed but not required). When the client cannot be sure that the resource identities are unique, it will have assign new ids to the resources. In practice this means that the client needs to keep some kind of identity translation table and update references to the resources it has copied locally when other resources are received.
The case of a gateway system that migrates resources from one ecosystem to another is very similar. In some limited cases, it can leave the logical id of the resources unchanged as resources are copied from one closed system to another. However, in more complicated cases, it will have to modify the resource references as resources pass across the gateway.
In its current form, FHIR allows various convenient implementer practices around resource identification that make integration across eco-system boundaries more difficult. In particular, either only allowing UUIDs for logical IDs or only allowing absolute references would make the boundary management problem easier but remove useful and convenient flexibility for other uses of FHIR. Implementers should design their systems accordingly.FHIR supports managing healthcare data in a distributed environment where different records reside on different servers - and reference data on remote servers. Implementers working in such a multi-server environment should be aware of several considerations:
In an environment where relevant information may be distributed across multiple servers, client systems in the space will need a mechanism to determine where data is located. There are three primary mechanisms for discovery:
A typical multi-server environment is one where each server is responsible for different "types" of information. For example, one server handles lab information, one handles medication information, one handles imaging information, etc. In this scenario, it's not uncommon for each of the servers to also hold their own records for certain types of resources such as Patient, Practitioner, Location, etc. When exploring linking the systems together, consideration needs to be given for how to manage such shared entities. Possibilities include:
identifier
values (e.g. shared medical record numbers, license numbers, etc.)Choice of architecture will be driven by the capabilities of the systems involved, needs of organizational workflow, ability to integrate legacy data, etc. Other architectures are possible, and mixtures of architectures are possible too. For example, a shared registry for patients while using linked resources for practitioners; shared registry for "licensed" practitioners while independent resources for administrative staff; etc.
Where synchronization is required, this can be done by polling, by having the "master" system pushing updates to other systems, by using FHIR's Subscription mechanism, through database synchronization technologies or other means. Design decisions will also be necessary around synchronization frequency, managing merges in a distributed environment, etc.
FHIR defines several mechanisms for retrieving resources that are referenced by other resources: _include and _revinclude, compartments and special operations such as Patient/$everything. Query behavior becomes more complex when references span servers. Servers returning _include and _revinclude capabilities can but need not return data stored from other servers. Similarly, the definition of compartments doesn't strictly limit data to that found on a single server, but servers are certainly not expected to include data found elsewhere when performing compartment-based queries or operations. As a result, query behavior may be different if data is stored on the same server as the referencing resource vs. stored on a different server.
For servers that support cross-server search, there are multiple considerations:
In some multi-server environments, there may be multiple servers that support storing a particular resource type. For example, one server might store lab-related observations, another might store vital sign observations and yet another could store clinician assessment observations. Alternatively, both the ordering system and the pharmacy system might have records of patient allergies. Clients will need rules to determine which server (or servers) a given record should be created on. Ideally, servers should also have logic that allows them to detect if a record has been submitted to them inappropriately. Such rules could be based on originating client, associated encounter, record type or other considerations.
Maturity Level: 0 (Draft) | 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 will be an 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. Whitespace is any Unicode whitespace, and the particular whitespace used is not significant, 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 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]"
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]" (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 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]" // 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 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 groupIn 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 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 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 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 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 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 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 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 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 specify a variable, the rule can be written in an abbreviated form:
"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)
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 "for types" or "for type+types" and checking the types of their inputs
looking for a match to the type of src.element, and [type]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 "for type+types" for a match to the type of
src.elementtodo
todo
Maturity Level: N/A | Standards Status:Informative |
This tutorial introduces the FHIR mapping language.
To start with, we're going to consider a very simple case: mapping between two structures that have the same definition, a single element with the same name and the same primitive type:
Source Structure | Target Structure |
TLeft a : string [0..1] | TRight a : string [0..1] |
The left instance is transformed to the right instance by copying a to a |
Note that for clarity in this tutorial, all the types are prefixed with T.
The first task to do is to set up the mapping context on a default group. All mappings are divided up into a set of groups. For now, we just set up a group named "tutorial" - the same as the name of the mapping. For this tutorial, we also declaring the source and target models, and specify that an application invokes this with a copy of the left (source) instance, and also an empty copy of the right (target) instance:
map "http://hl7.org/fhir/StructureMap/tutorial" = tutorial uses "http://hl7.org/fhir/StructureDefinition/tutorial-left" as source uses "http://hl7.org/fhir/StructureDefinition/tutorial-right" as target group tutorial input "source" : TLeft as source input "target" : TRight as target // rules go here endgroup
Note that the way the input variables are set up is a choice: we choose to provide the underlying type definitions on which both source and target models are based, and we choose to specify that the invoking application most provide both the source and the target instance trees. Other options are possible; these are discussed further below. The rest of the tutorial examples use the same setup for the group.
Having set up the context, we now need to define the relationships between the source and target structures:
"rule_a" : for source.a as a make target.a = a
This simple statement says that:
"rule_a" is a purely arbitrary name associated with the rule that appears in logs, error messages, trace files, etc. It has no other meaning in the mapping statements.
Note that there is no types explicitly in this mapping statement, but if the underlying system has types, then the types will have to be correct. If the underlying source and target trees are strongly typed, and the mapping groups have explicit types, then a short hand form is possible:
"rule_a" : for source.a make target.a
How this works is described below.
Now consider the case where the elements have different names:
Source Structure | Target Structure |
TLeft a1 : string [0..1] | TRight a2 : string [0..1] |
The left instance is transformed to the right instance by copying a1 to a2 |
This relationship is a simple variation of the last:
"rule_a1" : for source.a1 as b make target.a2 = b
Note that the choice of variable name is purely arbitrary. It does not need to be the same as the element name.
Still sticking with very simple mappings, let's consider the case where there is a length restriction on the target model that is shorter than the one on the source model - in this case, 20 characters.
Source Structure | Target Structure |
TLeft a2 : string [0..1] | TRight a2 : string [0..1] {maxlength = 20} |
The left instance is transformed to the right instance by copying a2 to a2, but target.a2 can only be 20 characters long |
There are 3 different ways to express this mapping, depending on what should happen when the length of source.a is > 20 characters:
"rule_a20a" : for source.a2 as a make target.a2 = truncate(a, 20) // just cut it off at 20 characters "rule_a20b" : for source.a2 as a where a2.length <= 20 make target.a2 = a // ignore it "rule_a20c" : for source.a2 as a check a2.length <= 20 make target.a2 = a // error if it's longer than 20 characters
Note that it is implicit here that the transformation engine is not required to expected to validate the output against that underlying structure definitions that may apply to it. An application may - and usually should - validate the outputs after the transforms, but the transform engine itself does not automatically validate the output (e.g. it does not assume that it's the final step in the process).
Now for the case where there is a simple type conversion between the primitive types on the left and right, in this case from a string to an integer.
Source Structure | Target Structure |
TLeft a21 : string [0..1] | TRight a21 : integer [0..1] |
The left instance is transformed to the right instance by copying a21 to a21, but a21 is converted to an integer |
There are 3 different ways to express this mapping, depending on what should happen when a is not an integer:
"rule_a21a" : for source.a21 as a make target.a21 = cast(a, "integer") // error if it's not an integer "rule_a21b" : for source.a21 as a where a.isInteger make target.a21 = cast(a, "integer") // ignore it "rule_a21c" : for source.a21 as a where not at1.isInteger make target.a21 = 0 // just assign it 0
More than one of these mapping rules may be present to handle all possible cases - e.g. rule_a21b combined with rule_a21c.
Note that the mapping language does not itself define which primitive types exist. Typically, primitive types are defined by the underlying type system for the source and target trees, and the implementation layer makes these types available to the mapping language using the FHIRPath primitive types. The mapping language uses the FHIRPath syntax for primitive constants.
Back to the simple case where source.a22 is copied to target.a22, but in this case, a22 can repeat (in both source and target):
Source Structure | Target Structure |
TLeft a22 : string [0..*] | TRight a22 : string [0..*] |
The left instance is transformed to the right instance by copying a22 to a22, once for each copy of a22 |
The transform rule simply asserts that a22 maps to a22. The engine will apply the rule once for each instance of a22:
"rule_a22" : for source.a22 as a make target.a22 = a
This will create one a22 in TRight for each a22 in TLeft.
A more difficult case is where the source allows multiple repeats, but the target doesn't:
Source Structure | Target Structure |
TLeft a23 : string [0..*] | TRight a23 : integer [0..1] |
The left instance is transformed to the right instance by copying a23 to a23, but there can only be one copy of a23 |
Again, there are multiple different ways to write this, depending on out desired outcome if there is more than one copy of a23:
rule_a23a : for source.a23 as a make target.a23 = a // leave it to the transform engine rule_a23a : for source.a23 only_one as a make target.a23 = a // transform engine throws an error if there is more than one rule_a23b : for source.a23 first as a make target.a23 = a // Only use the first one rule_a23b : for source.a23 last as a make target.a23 = a // Only use the last one
Leaving the outcome to the transform engine is not recommended; it might not always know whether a property is confined to a single value, and exactly what happens is unpredictable. However, there are some circumstances where the appropriate action is to defer resolution, so this is allowed.
Most transformations involve nested content. Let's start with a simple case, where element aa contains ab:
Source Structure | Target Structure |
TLeft aa : [0..*] ab : string [1..1] | TRight aa : [0..*] ab : string [1..1] |
The left instance is transformed to the right instance by copying aa to aa, and within aa, ab to ab |
Note that there is no specified type for the element aa. Some structure definitions (FHIR resources) do leave these elements as anonymously typed, while others explicitly type them. However, since the mapping does not refer to the type, it's literal type is not important.
rule_aa : for source.aa as s_aa make target.aa as t_aa then { // make aa exist rule_ab : for s_aa.ab as ab make t_aa.ab = ab // copy ab inside aa }
This situation is handled by a pair of rules: the first rule establishes that relationship between source.aa and target.aa, and assigns 2 variable names to them. Then, the rule contains an additional set of rules (though only one in this example) to map with the context of s_aa and t_aa.
An alternate approach is to move the dependent rules to their own group:
rule_aa : for source.aa as s_aa make target.aa as t_aa then ab_content(s_aa, t_aa) // make aa exist group ab_content input src as source input tgt as target rule_ab : for src.ab as ab make tgt.ab = ab // copy ab inside aa }
Note that variables are divided into source and target; source variables are read-only, and cannot have their properties changed. Variable names may be reused in different contexts - they are only valid within the group or rule that defines them, and any dependent rules or groups.
A common translation pattern is to perform a translation e.g. from one set of codes to another
Source Structure | Target Structure |
TLeft d : code [0..1] | TRight d : code [0..1] |
The left instance is transformed to the right instance by translating source.d from one set of codes to another |
The key to this transformation is the ConceptMap resource, which actually specifies the mapping from one set of codes to the other:
rule_d : for source.d as d make target.d = translate(d, 'uri-of-concept-map', 'code')
This asks the mapping engine to use the $translate operation on the terminology server to translate the code using a specified concept map, and then to put the code value of the return translation in target.d.
Another common translation is where the target mapping for one element depends on the value of another element.
Source Structure | Target Structure |
TLeft i : string [0..1] m : integer [1..1] | TRight j : [0..1] k : [0..1] |
How the left instance is transformed to the right instance depends on the value of m: if m < 2, then i maps to j, else it maps to k |
This is managed using FHIRPath conditions on the mapping statements:
rule_i1 : for source.i as i where m < 2 make target.j = i rule_i2 : for source.i as i where m >= 2 make target.k = i
Many/most trees are fully and strongly typed. In these cases, the mapping language can make use of the typing system to simplify the mapping statements.
Source Structure | Target Structure |
TLeft aa : TLeftInner [0..*] TLeftInner ab : string [1..1] | TRight aa : : TRightInner [0..*] TRightInner ab : string [1..1] |
The left instance is transformed to the right instance by copying aa to aa, and within aa, ab to ab |
This is the same case as Step 7 above, but the mapping statements take advantage of the types:
rule_aa : for source.aa make target.aa group for types ab_content input src : TLeftInner as source input tgt : TRightInner as target rule_ab : for src.ab make tgt.ab }
There are 2 different things happening in this short form:
group for types
- the "for types" indicates that this group is the default group to apply any time an element of type TLeftInner is mapped to a TRightInner
In the case of the first rule (rule_aa), the engine finds a need to map aa
to aa
and determines that it must map from TLeftInner to a TRightInner.
Since a group is defined for this purpose, it creates a TRightInner in target.aa, and then applies the discovered rule as a dependency rule. Inside
that rule that instructs the mapping engine to make tgt.ab from src.ab. It knows that both are primitive types, and compatible, and can apply this correctly.
This short form is only applicable when there is only one source and target, when the types of both are known, and when no other dependency rules
are nominated.
If the target element is polymorphic (can have more than one type), then the correct type of the target can only be inferred from the source type:
group for type+types ab_content input src : TLeftInner as source input tgt : TRightInner as target rule_ab : for src.ab make tgt.ab }
Not only is this group the default for (TLeftInner:TRightInner), if the engine has a TLeftInner with an unknown target type, it should create a TRightInner, and proceed as above.
It is an error if the engine locates more than one group of rules claiming to be the correct group for a type pair of a single source type.
It's now time to start moving away from relatively simple cases to some of the harder ones to manage mappings for. The first mixes list management, and converting from a specific structure to a general structure:
Source Structure | Target Structure |
TLeft e : string [0..*] f : string [1..1] | TRight e : [0..*] f : string [1..1] g : code [1..1] |
The left instance is transformed to the right instance by adding one instance of target.e for each source.e, where the value goes into target.e.f, and the value of target.e.g is 'g1'. source.f is also transformed into the same structure, but the value of target.e.g is 'g2'. As an added complication, the value for source.f must come first |
This leads to some more complex mapping statements:
ef_a1: for source.e as s_e make target.e as t_e then { ef_a2: for s_e make t_e.f = s_e, t_e.g = "g1" } ef_b1: for source.f as s_f make target.e as t_e { first } then { ef_b2: for s_f make t_e.f = s_f, t_e.g = "g2" }
The second example for reworking structure moves cardinality around the hierarchy. in this case, the source has an optional structure that contains a repeating structure, while the target puts the cardinality at the next level up:
Source Structure | Target Structure |
TLeft az1 :[0..1] az2 : string [1..1] az3 : string [0..*] | TRight az1 :[0..*] az2 : string [1..1] az3 : string [0..1] |
The left instance is transformed to the right instance creating on target.az1 for every source.az1.az3, and then populating each az1 with the matching value of az3, and copying the value of az2 to each instance |
The key to setting this mapping up is to create a variable context for source.az1, and then carry it down, performing the actual mappings at the next level down:
// setting up a variable for the parent aza : for src.az1 as s_az1 then { // one target.az1 for each az3 azb : for s_az1.az3 as s_az3 make target.az1 as t_az1 then { // value for az2. Note that this refers to a previous context in the source az2 : for s_az1.az2 as az2 make t_az1.az2 = az2 // value for az3 az3 : for s_az3 make tgt_az1.az3 = src_az3 } }
Simple mappings, such as we've dealt with so far, where the source and target structure both have the same scope, and there is only one of each, are all well and good, but there are many mappings where this is not the case. There is a set of complications when dealing with multiple instances:
For our first example, we're going to look at creating multiple output structures from a single input structure.
Source Structure | Target Structure |
TLeft f1 : String [0..*]; | TRight ptr : Resource(TRight2) [0..*] TRight2 f1 : String [1..1]; |
The left instance is transformed to the right instance creating a copy of TRight2 for each f1 in the source, and then putting the value of source.f1 in TRight2.f1 |
The key to setting this mapping up is to create a variable context for source.az1, and then carry it down, performing the actual mappings at the next level down:
f1 : for source.f1 as s_f1 make create("TRight2") as rr, target.ptr = reference(rr) then { f1a: for s_f1 make rr.f2 = srcff }
This mapping statement makes use a special known value "null" for the target context to indicate that the created element/object of type "TRight2" doesn't get added to any existing target context. Instead, it will only be available as a context in which to perform further mappings - as rule f1a does.
The mapping engine passes the create request through to the host application, which is using the mapping. It must create a valid instance of TRight, and identify it as appropriate for the technical context in which the mapping is being used. The reference transform is also passed back to the host application for it to determine how to represent the reference - but this is usually some kind of URL.
For our second example, we're going to look at the reverse: where multiple input structures create a single input structure.
Source Structure | Target Structure |
TLeft ptr : Resource(TLeft2) [0..*] TLeft2 f2 : String [0..*]; | TRight f2 : String [1..*]; |
The left instance is transformed to the right instance finding each ptr reference, getting its value for f1, and adding this in target.f2 |
The first task of the map is to ask the application host to find the structure identified by source.ptr, and create a variable for it
f2: [todo]
f2: [todo]
Maturity Level: n/a | Standards Status:Informative |
For many of the resources and other content defined in this specification, mappings are provided to other standards. These mappings are informative and are provided to help implementers of this specification understand the content and use it correctly.
Due to wide variation in the implementation of standards in healthcare, none of the mappings are authoritative or normative, and implementers always have to check local usage before using these mappings in production.
This table summarizes the mappings that may be provided through the specification: