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();
}
}
Related Articles
Explore HL7 message creation in depth:
Java (HAPI):
- HL7 Programming using Java and HAPI - Creating HL7 Messages - Complete creation examples
- HL7 Programming using Java and HAPI - Creating ACK Messages - Acknowledgment handling
- HL7 Programming using Java and HAPI - Handling Binary Data - ED segment handling
.NET (NHAPI):
- HL7 Programming using .NET and NHAPI - Creating HL7 Messages - .NET message creation
- HL7 Programming using .NET and NHAPI - Creating ACK Messages - .NET acknowledgments