Creating HL7 V2 Messages with HAPI

Section 8 of 14
57% complete

Basic Message Creation

Creating HL7 messages programmatically involves instantiating message objects, populating segments with data, and encoding the result to a string for transmission. HAPI provides strongly-typed classes for each message type, ensuring compile-time safety and making it easier to discover available fields. The general workflow is: create context, instantiate message, populate segments, encode, and close context.

Example 1: Creating an ADT^A01 Message (V2.5)

This example demonstrates creating a complete patient admission message. The ADT^A01 message requires MSH (header), PID (patient identification), and PV1 (patient visit) segments at minimum. The initQuickstart() method initializes the message with basic header information, while individual segment accessors allow you to populate patient demographics, location, and provider information.

import ca.uhn.hl7v2.DefaultHapiContext;
import ca.uhn.hl7v2.HapiContext;
import ca.uhn.hl7v2.model.v25.message.ADT_A01;
import ca.uhn.hl7v2.model.v25.segment.MSH;
import ca.uhn.hl7v2.model.v25.segment.PID;
import ca.uhn.hl7v2.model.v25.segment.PV1;
import ca.uhn.hl7v2.parser.Parser;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

public class CreateADTA01Example {

    public static void main(String[] args) {
        try {
            // Create HAPI context
            HapiContext context = new DefaultHapiContext();

            // Create ADT_A01 message
            ADT_A01 adtMessage = new ADT_A01();
            adtMessage.initQuickstart("ADT", "A01", "P");

            // Populate MSH segment
            MSH msh = adtMessage.getMSH();
            msh.getSendingApplication().getNamespaceID().setValue("SENDING_APP");
            msh.getSendingFacility().getNamespaceID().setValue("SENDING_FACILITY");
            msh.getReceivingApplication().getNamespaceID().setValue("RECEIVING_APP");
            msh.getReceivingFacility().getNamespaceID().setValue("RECEIVING_FACILITY");

            String timestamp = LocalDateTime.now()
                .format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
            msh.getDateTimeOfMessage().getTime().setValue(timestamp);
            msh.getMessageControlID().setValue("MSG" + System.currentTimeMillis());

            // Populate PID segment
            PID pid = adtMessage.getPID();
            pid.getPatientID().getIDNumber().setValue("123456");
            pid.getPatientIdentifierList(0).getIDNumber().setValue("123456");
            pid.getPatientIdentifierList(0).getAssigningAuthority()
                .getNamespaceID().setValue("HOSPITAL");
            pid.getPatientIdentifierList(0).getIdentifierTypeCode().setValue("MR");

            // Patient name
            pid.getPatientName(0).getFamilyName().getSurname().setValue("DOE");
            pid.getPatientName(0).getGivenName().setValue("JOHN");
            pid.getPatientName(0).getSecondAndFurtherGivenNamesOrInitialsThereof()
                .setValue("A");

            // Date of birth
            pid.getDateTimeOfBirth().getTime().setValue("19800115");

            // Gender
            pid.getAdministrativeSex().setValue("M");

            // Address
            pid.getPatientAddress(0).getStreetAddress().getStreetOrMailingAddress()
                .setValue("123 MAIN ST");
            pid.getPatientAddress(0).getCity().setValue("CITY");
            pid.getPatientAddress(0).getStateOrProvince().setValue("ST");
            pid.getPatientAddress(0).getZipOrPostalCode().setValue("12345");
            pid.getPatientAddress(0).getCountry().setValue("USA");

            // Phone number
            pid.getPhoneNumberHome(0).getTelephoneNumber().setValue("5555555555");

            // Marital status
            pid.getMaritalStatus().getIdentifier().setValue("M");

            // SSN
            pid.getSSNNumberPatient().setValue("123456789");

            // Populate PV1 segment
            PV1 pv1 = adtMessage.getPV1();
            pv1.getSetIDPV1().setValue("1");
            pv1.getPatientClass().setValue("I"); // Inpatient

            // Assigned patient location
            pv1.getAssignedPatientLocation().getPointOfCare().setValue("2000");
            pv1.getAssignedPatientLocation().getRoom().setValue("2012");
            pv1.getAssignedPatientLocation().getBed().setValue("01");

            // Attending doctor
            pv1.getAttendingDoctor(0).getIDNumber().setValue("004777");
            pv1.getAttendingDoctor(0).getFamilyName().getSurname().setValue("SMITH");
            pv1.getAttendingDoctor(0).getGivenName().setValue("JOHN");
            pv1.getAttendingDoctor(0).getPrefixEgDR().setValue("DR");

            // Hospital service
            pv1.getHospitalService().setValue("SUR");

            // Admission type
            pv1.getAdmissionType().setValue("ADM");

            // Encode message to string
            Parser parser = context.getPipeParser();
            String encodedMessage = parser.encode(adtMessage);

            System.out.println("Created HL7 V2.5 ADT^A01 Message:");
            System.out.println(encodedMessage);

            // Clean up
            context.close();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Example 2: Creating an ORU^R01 Lab Result Message

ORU messages have a more complex structure than ADT messages due to their nested groups. The PATIENT_RESULT group contains patient information, while ORDER_OBSERVATION groups contain the test order (OBR) and individual results (OBX segments). Each OBX segment represents one observation value with its units, reference range, and interpretation flag. Understanding this hierarchy is crucial for correctly building lab result messages.

import ca.uhn.hl7v2.DefaultHapiContext;
import ca.uhn.hl7v2.HapiContext;
import ca.uhn.hl7v2.model.v25.message.ORU_R01;
import ca.uhn.hl7v2.model.v25.segment.*;
import ca.uhn.hl7v2.model.v25.group.ORU_R01_ORDER_OBSERVATION;
import ca.uhn.hl7v2.model.v25.group.ORU_R01_PATIENT_RESULT;
import ca.uhn.hl7v2.parser.Parser;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

public class CreateORUR01Example {

    public static void main(String[] args) {
        try {
            HapiContext context = new DefaultHapiContext();

            // Create ORU_R01 message
            ORU_R01 oruMessage = new ORU_R01();
            oruMessage.initQuickstart("ORU", "R01", "P");

            // Populate MSH
            MSH msh = oruMessage.getMSH();
            msh.getSendingApplication().getNamespaceID().setValue("LAB");
            msh.getSendingFacility().getNamespaceID().setValue("HOSPITAL");
            msh.getReceivingApplication().getNamespaceID().setValue("RECEIVER");
            msh.getReceivingFacility().getNamespaceID().setValue("HOSPITAL");

            String timestamp = LocalDateTime.now()
                .format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
            msh.getDateTimeOfMessage().getTime().setValue(timestamp);
            msh.getMessageControlID().setValue("MSG" + System.currentTimeMillis());

            // Get patient result group
            ORU_R01_PATIENT_RESULT patientResult = oruMessage.getPATIENT_RESULT();

            // Populate PID
            PID pid = patientResult.getPATIENT().getPID();
            pid.getPatientID().getIDNumber().setValue("123456");
            pid.getPatientIdentifierList(0).getIDNumber().setValue("123456");
            pid.getPatientIdentifierList(0).getAssigningAuthority()
                .getNamespaceID().setValue("HOSPITAL");
            pid.getPatientIdentifierList(0).getIdentifierTypeCode().setValue("MR");

            pid.getPatientName(0).getFamilyName().getSurname().setValue("DOE");
            pid.getPatientName(0).getGivenName().setValue("JOHN");
            pid.getDateTimeOfBirth().getTime().setValue("19800115");
            pid.getAdministrativeSex().setValue("M");

            // Get order observation group
            ORU_R01_ORDER_OBSERVATION orderObservation =
                patientResult.getORDER_OBSERVATION();

            // Populate OBR (Observation Request)
            OBR obr = orderObservation.getOBR();
            obr.getSetIDOBR().setValue("1");
            obr.getPlacerOrderNumber().getEntityIdentifier().setValue("ORD123456");
            obr.getUniversalServiceIdentifier().getIdentifier().setValue("CBC");
            obr.getUniversalServiceIdentifier().getText()
                .setValue("COMPLETE BLOOD COUNT");
            obr.getUniversalServiceIdentifier().getNameOfCodingSystem()
                .setValue("LOCAL");

            String orderTime = LocalDateTime.now().minusHours(2)
                .format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
            obr.getObservationDateTime().getTime().setValue(orderTime);

            String resultTime = LocalDateTime.now()
                .format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
            obr.getObservationEndDateTime().getTime().setValue(resultTime);

            // Ordering provider
            obr.getOrderingProvider(0).getIDNumber().setValue("004777");
            obr.getOrderingProvider(0).getFamilyName().getSurname().setValue("SMITH");
            obr.getOrderingProvider(0).getGivenName().setValue("JOHN");
            obr.getOrderingProvider(0).getPrefixEgDR().setValue("DR");

            // Add OBX segments (Observations)

            // WBC
            OBX obx1 = orderObservation.getOBSERVATION(0).getOBX();
            obx1.getSetIDOBX().setValue("1");
            obx1.getValueType().setValue("NM");
            obx1.getObservationIdentifier().getIdentifier().setValue("WBC");
            obx1.getObservationIdentifier().getText().setValue("White Blood Count");
            obx1.getObservationIdentifier().getNameOfCodingSystem().setValue("LOCAL");
            obx1.getObservationValue(0).setData(
                new ca.uhn.hl7v2.model.primitive.ST(oruMessage)
            );
            ((ca.uhn.hl7v2.model.primitive.ST) obx1.getObservationValue(0).getData())
                .setValue("7.5");
            obx1.getUnits().getIdentifier().setValue("10*3/uL");
            obx1.getReferencesRange().setValue("4.5-11.0");
            obx1.getAbnormalFlags(0).setValue("N");
            obx1.getObservationResultStatus().setValue("F");

            // RBC
            OBX obx2 = orderObservation.getOBSERVATION(1).getOBX();
            obx2.getSetIDOBX().setValue("2");
            obx2.getValueType().setValue("NM");
            obx2.getObservationIdentifier().getIdentifier().setValue("RBC");
            obx2.getObservationIdentifier().getText().setValue("Red Blood Count");
            obx2.getObservationIdentifier().getNameOfCodingSystem().setValue("LOCAL");
            obx2.getObservationValue(0).setData(
                new ca.uhn.hl7v2.model.primitive.ST(oruMessage)
            );
            ((ca.uhn.hl7v2.model.primitive.ST) obx2.getObservationValue(0).getData())
                .setValue("4.8");
            obx2.getUnits().getIdentifier().setValue("10*6/uL");
            obx2.getReferencesRange().setValue("4.5-5.5");
            obx2.getAbnormalFlags(0).setValue("N");
            obx2.getObservationResultStatus().setValue("F");

            // Hemoglobin
            OBX obx3 = orderObservation.getOBSERVATION(2).getOBX();
            obx3.getSetIDOBX().setValue("3");
            obx3.getValueType().setValue("NM");
            obx3.getObservationIdentifier().getIdentifier().setValue("HGB");
            obx3.getObservationIdentifier().getText().setValue("Hemoglobin");
            obx3.getObservationIdentifier().getNameOfCodingSystem().setValue("LOCAL");
            obx3.getObservationValue(0).setData(
                new ca.uhn.hl7v2.model.primitive.ST(oruMessage)
            );
            ((ca.uhn.hl7v2.model.primitive.ST) obx3.getObservationValue(0).getData())
                .setValue("14.5");
            obx3.getUnits().getIdentifier().setValue("g/dL");
            obx3.getReferencesRange().setValue("13.5-17.5");
            obx3.getAbnormalFlags(0).setValue("N");
            obx3.getObservationResultStatus().setValue("F");

            // Encode message
            Parser parser = context.getPipeParser();
            String encodedMessage = parser.encode(oruMessage);

            System.out.println("Created HL7 V2.5 ORU^R01 Message:");
            System.out.println(encodedMessage);

            context.close();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Example 3: Creating an ORM^O01 Order Message

Order messages use the ORM structure which contains PATIENT and ORDER groups. The ORC (Common Order) segment specifies the order control code indicating whether this is a new order, modification, or cancellation. The OBR segment within ORDER_DETAIL describes what is being ordered. This separation allows the same message structure to handle various order lifecycle events.

import ca.uhn.hl7v2.DefaultHapiContext;
import ca.uhn.hl7v2.HapiContext;
import ca.uhn.hl7v2.model.v25.message.ORM_O01;
import ca.uhn.hl7v2.model.v25.segment.*;
import ca.uhn.hl7v2.model.v25.group.ORM_O01_ORDER;
import ca.uhn.hl7v2.parser.Parser;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

public class CreateORMO01Example {

    public static void main(String[] args) {
        try {
            HapiContext context = new DefaultHapiContext();

            // Create ORM_O01 message
            ORM_O01 ormMessage = new ORM_O01();
            ormMessage.initQuickstart("ORM", "O01", "P");

            // Populate MSH
            MSH msh = ormMessage.getMSH();
            msh.getSendingApplication().getNamespaceID().setValue("CPOE");
            msh.getSendingFacility().getNamespaceID().setValue("HOSPITAL");
            msh.getReceivingApplication().getNamespaceID().setValue("LAB");
            msh.getReceivingFacility().getNamespaceID().setValue("HOSPITAL");

            String timestamp = LocalDateTime.now()
                .format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
            msh.getDateTimeOfMessage().getTime().setValue(timestamp);
            msh.getMessageControlID().setValue("MSG" + System.currentTimeMillis());

            // Populate PID
            PID pid = ormMessage.getPATIENT().getPID();
            pid.getPatientID().getIDNumber().setValue("123456");
            pid.getPatientIdentifierList(0).getIDNumber().setValue("123456");
            pid.getPatientIdentifierList(0).getAssigningAuthority()
                .getNamespaceID().setValue("HOSPITAL");
            pid.getPatientIdentifierList(0).getIdentifierTypeCode().setValue("MR");

            pid.getPatientName(0).getFamilyName().getSurname().setValue("DOE");
            pid.getPatientName(0).getGivenName().setValue("JOHN");
            pid.getDateTimeOfBirth().getTime().setValue("19800115");
            pid.getAdministrativeSex().setValue("M");

            // Get order group
            ORM_O01_ORDER order = ormMessage.getORDER();

            // Populate ORC (Common Order)
            ORC orc = order.getORC();
            orc.getOrderControl().setValue("NW"); // New order
            orc.getPlacerOrderNumber().getEntityIdentifier().setValue("ORD123456");

            String orderTime = LocalDateTime.now()
                .format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
            orc.getDateTimeOfTransaction().getTime().setValue(orderTime);

            // Populate OBR (Observation Request)
            OBR obr = order.getORDER_DETAIL().getOBR();
            obr.getSetIDOBR().setValue("1");
            obr.getPlacerOrderNumber().getEntityIdentifier().setValue("ORD123456");

            // Universal service identifier - Lab test being ordered
            obr.getUniversalServiceIdentifier().getIdentifier().setValue("CBC");
            obr.getUniversalServiceIdentifier().getText()
                .setValue("COMPLETE BLOOD COUNT");
            obr.getUniversalServiceIdentifier().getNameOfCodingSystem()
                .setValue("LOCAL");

            obr.getObservationDateTime().getTime().setValue(orderTime);

            // Encode message
            Parser parser = context.getPipeParser();
            String encodedMessage = parser.encode(ormMessage);

            System.out.println("Created HL7 V2.5 ORM^O01 Message:");
            System.out.println(encodedMessage);

            context.close();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Working with Different HL7 Versions

When your application needs to support multiple HL7 versions, you must use the appropriate version-specific classes from HAPI. Each version has its own package (v23, v24, v25, etc.) with message and segment classes that match that version’s specification. The structure of messages can differ between versions, so you cannot simply cast a V2.3 message to a V2.5 type. Use fully qualified class names to make version differences explicit in your code.

import ca.uhn.hl7v2.DefaultHapiContext;
import ca.uhn.hl7v2.HapiContext;
import ca.uhn.hl7v2.parser.Parser;

public class MultiVersionExample {

    public void createV23Message() throws Exception {
        HapiContext context = new DefaultHapiContext();

        // V2.3 message
        ca.uhn.hl7v2.model.v23.message.ADT_A01 adtV23 =
            new ca.uhn.hl7v2.model.v23.message.ADT_A01();
        adtV23.initQuickstart("ADT", "A01", "P");

        // Work with V2.3 specific structure
        ca.uhn.hl7v2.model.v23.segment.MSH msh = adtV23.getMSH();
        msh.getMessageType().getMessageType().setValue("ADT");
        msh.getMessageType().getTriggerEvent().setValue("A01");
        msh.getVersionID().setValue("2.3");

        Parser parser = context.getPipeParser();
        String encoded = parser.encode(adtV23);
        System.out.println("V2.3 Message: " + encoded);

        context.close();
    }

    public void createV25Message() throws Exception {
        HapiContext context = new DefaultHapiContext();

        // V2.5 message
        ca.uhn.hl7v2.model.v25.message.ADT_A01 adtV25 =
            new ca.uhn.hl7v2.model.v25.message.ADT_A01();
        adtV25.initQuickstart("ADT", "A01", "P");

        ca.uhn.hl7v2.model.v25.segment.MSH msh = adtV25.getMSH();
        msh.getVersionID().getVersionID().setValue("2.5");

        Parser parser = context.getPipeParser();
        String encoded = parser.encode(adtV25);
        System.out.println("V2.5 Message: " + encoded);

        context.close();
    }
}

Explore HL7 message creation in depth:

Java (HAPI):

.NET (NHAPI):

Quiz: Creating HL7 V2 Messages with HAPI

Question 1 of 5

What method is used to quickly initialize a new HL7 message with basic header information?