Working with HL7
Normalize parsed HL7 before mapping and implement customer-specific workflows around the generated FHIR Bundle.
FireSonic keeps deterministic, generally applicable HL7 → FHIR mapping in the Java platform core. Customer-specific behavior belongs in Groovy: normalize source HL7 in preConvert(), then apply organization-specific workflow logic or FHIR enrichment in postConvert().
Use the built-in mapper for generic interoperability. Use Groovy helpers and workflow classes for behavior that depends on the customer's local HL7 conventions, FHIR server state, location model, merge policy, terminology, or operational rules.
HL7Helper
HL7Helper provides defensive, null-tolerant helpers for common HL7 operations so customer scripts do not need to work directly with low-level HAPI APIs for every field.
| Operation | Examples |
|---|---|
| Read / write known paths | get(), set(), copy(), isEmpty() |
| Read real Segment objects | safeGet(), setFieldEncoded(), setFieldComponent() |
| Clear content | clear(), clearRepeatedSegments(), removeSegments() |
| Message metadata | getMessageCode(), getTriggerEvent(), getMessageControlId() |
| Date/time conversion | readTS(), readDT(), resolveEventDateTime() |
def msg = envelope.hl7Message
String patientId = HL7Helper.get(msg, "/PID-3-1")
String trigger = HL7Helper.getTriggerEvent(msg)
HL7Helper.set(msg, "/PID-8", "F")
Known paths vs repeated or nested segments
Use Terser-style paths through HL7Helper when the path is known and stable. Repeating groups are different: messages such as ORU can contain OBR and OBX segments inside nested HAPI groups, so guessing group paths is fragile.
String visit = HL7Helper.get(msg, "/PV1-19-1")
HL7Helper.set(msg, "/OBR-3-1", "3510540058")HL7SegmentScanner.byName(msg, "OBX").each { ref ->
String code = HL7Helper.safeGet(ref.segment, 3, 1)
}HL7SegmentScanner and HL7SegmentRef
HL7SegmentScanner recursively walks the parsed HAPI group tree and returns a flat snapshot of real segment occurrences in traversal order. Each HL7SegmentRef exposes the segment index, name, actual HAPI segment, owning group, and zero-based repetition.
for (HL7SegmentRef ref : HL7SegmentScanner.scan(msg)) {
if (ref.name == "OBX") {
String value = HL7Helper.safeGet(ref.segment, 5)
}
}
Clearing fields and segments
HL7Helper.clear(msg, "/PV1-20")
HL7Helper.clearRepeatedSegments(msg, "IN1")
HL7Helper.removeSegments(msg, "DG1") { dg1 ->
HL7Helper.safeGet(dg1, 3, 1) == "B34.2"
}
Segment helpers clear matching segment content; they do not promise physical removal from HAPI's internal group metadata. FireSonic mappers skip empty segments.
Timezone normalization
HL7TimezoneNormalizer adds a configured default timezone only when an HL7 TS/DTM value contains time precision and no timezone. Date-only values and values that already carry an offset remain unchanged.
HL7TimezoneNormalizer.applyTimezone(
msg,
"+0200",
["/MSH-7", "/EVN-2-1", "/PV1-44-1"]
)
HL7TimezoneNormalizer.applyTimezoneRepeated(
msg,
"OBR",
[6, 7, 8, 14, 22],
"+0200"
)
MessageEnvelope mutability
Treat rawHL7 as the immutable audit copy. Modify or replace the parsed envelope.hl7Message in preConvert().
Complex customer workflows
Not every HL7 event can be handled correctly by a universal mapper. FireSonic keeps customer-dependent behavior in Groovy workflow classes that operate around the mapped Bundle and can use managed SDK services.
ADT^A02 Patient Transfer
Patient transfer behavior depends heavily on how each organization models locations and encounter history. The reference workflow demonstrates a practical default that can be adapted to the customer's own rules.
- Find the existing Encounter by identifier.
- Merge the existing and newly mapped location history.
- Preserve locations that did not change.
- Close locations that are no longer present.
- Activate newly mapped locations.
- Replace the default contained-location behavior when the customer uses a different Location model.
ADT^A40 Patient Merge
Patient merge is an identity workflow rather than a simple field mapping. The reference workflow coordinates the merge with the target FHIR server while leaving customer-specific survivorship policy customizable.
- Resolve the survivor and deprecated Patients.
- Apply merge semantics and Patient links.
- Post the merge transaction.
- Relink dependent resources in batches.
- Retry transient failures through SDK support utilities.
- Keep demographic survivorship rules customer-specific.
Main script as orchestrator
The injected services live in the main IntegrationScript. Helper workflows receive the services they need explicitly.
if ("A02" == envelope.triggerEvent) {
return new PatientTransferWorkflow(fhir)
.applyTransferMerge(envelope, bundle)
}
if ("A40" == envelope.triggerEvent) {
return new PatientMergeWorkflow(fhir, support)
.applyPatientMerge(envelope, bundle)
}
Explore the SDK API in your IDE
The SDK exposes public API types such as IntegrationScript, MessageEnvelope, FhirClient, ScriptSupport, cache/HTTP/metrics APIs, and dependency-injection annotations. In IntelliJ IDEA or Eclipse, use autocomplete, Open Declaration, and Javadoc to inspect the available methods and contracts directly while writing Groovy scripts.
