HL7v2 to FHIR - customizing code mappings

Override standard HL7v2 code mappings, add your local table values and keep unknown codes preserved in the output.

HL7v2 and FHIR express the same concepts through different codes - an HL7 gender of M is FHIR's male, and a patient class of I is the encounter class IMP. The conversion carries the standard translation tables built in, so PID-8 gender, PV1-2 patient class, PID-16 marital status, order and result statuses and the other coded fields come out as FHIR codes without any setup.

What remains for you are the codes that are yours alone. A local table value needs one line in the configuration file, and a code with no mapping at all stays preserved in the output.

Overriding and adding codes

Hospital systems use their own local values - a patient class of P that means outpatient, a locally defined result status. The [codes] section of the configuration file adjusts any map, one subsection per map, each line mapping an HL7 code to the FHIR target code:

[codes]

# PV1-2 - P is our local code for ambulatory
[[patient_class]]
P=AMB

# PID-8 - map our local code D to unknown
[[administrative_sex]]
D=UNK

An override wins over the standard value for that code, all other codes in the map keep their standard translations, and codes new to the map are added. With the file above, msg.to_fhir(config='my-mappings') turns a PV1-2 of P into:

{
  "system": "http://terminology.hl7.org/CodeSystem/v3-ActCode",
  "code": "AMB"
}

The map names

The subsection names in [codes] are the map names below:

MapSource field
administrative_sexPID-8
marital_statusPID-16
name_typeXPN name type, e.g. PID-5.7
address_typeXAD address type, e.g. PID-11.7
telecom_useXTN use code, e.g. PID-13.2
telecom_equipment_typeXTN equipment type, e.g. PID-13.3
patient_classPV1-2
patient_class_statusPV1-2, deciding the Encounter status
allergy_categoryAL1-2
allergy_typeAL1-2
allergy_criticalityAL1-4
allergy_severityAL1-4
diagnosis_typeDG1-6
abnormal_flagsOBX-8
observation_result_statusOBX-11
order_statusORC-1
result_statusOBR-25
filler_statusSCH-25
completion_statusRXA-20

Unknown codes

A code with no mapping - standard or configured - does not stop the conversion and no value is invented in its place:

  • Optional elements, e.g. Patient.gender or Patient.maritalStatus, are left unset
  • Required elements get a safe default - an Observation status becomes unknown, an Encounter class becomes the null-flavor UNK

Either way the original code is preserved as an extension on the resource, naming the field it came from:

from zato.hl7v2 import parse_hl7

raw = (
    'MSH|^~\\&|SENDER|FACILITY|RECEIVER|FAC|20260315101112||ADT^A01^ADT_A01|CTL001|P|2.9\r'

    # PID-8 contains X, which is not a standard administrative sex code ..
    'PID|||12345^^^HOSP^MR||SMITH^JOHN||19800115|X\r'

    # .. and neither is Q a standard patient class in PV1-2.
    'PV1|1|Q\r'
)

msg = parse_hl7(raw, validate=False)
bundle = msg.to_fhir()

for entry in bundle.entry:
    resource = entry.resource.to_dict()
    if 'extension' in resource:
        for item in resource['extension']:
            print(item['url'], '->', item['valueString'])
urn:zato:hl7v2:extension/unmapped/PID-8 -> X
urn:zato:hl7v2:extension/unmapped/PV1-2 -> Q

The X sits on the Patient and the Q on the Encounter, so the values stay available for inspection - and the URLs point at the local codes worth adding to your [codes] section. The extensions share their base URL with Z-segment extensions and the [extensions] section of the configuration file changes it.

Verifying a conversion is complete

get_conversion_warnings returns the list of everything a conversion could neither map nor preserve. An empty list confirms the whole message made it into the bundle - a useful assertion in tests that convert your own traffic:

from zato.hl7.mappings import get_conversion_warnings

warnings = get_conversion_warnings(bundle)
assert warnings == []

See also

PageWhat it covers
ConfigurationThe .ini file the codes section lives in, with every other setting
Z-segmentsThe other kind of extension, built from custom segments
What you get per messageThe resources the coded fields end up on

Learn more


Schedule a meaningful demo

Book a demo with an expert who will help you build meaningful systems that match your ambitions

"We evaluated 12 integration platforms and Zato was the only one to score 100%."

Philip Zuñiga, Assistant Professor, University of the Philippines