Parsing HL7 V2 Messages

Section 9 of 14
64% complete

Basic Parsing

Parsing converts an HL7 message string into a structured Java object that you can navigate programmatically. The parser automatically detects the message version from MSH-12 and creates the appropriate typed message object. After parsing, you can use instanceof to determine the message type and cast to the specific class for type-safe access to segments. Always wrap parsing in try-catch blocks since malformed messages will throw HL7Exception.

import ca.uhn.hl7v2.DefaultHapiContext;
import ca.uhn.hl7v2.HapiContext;
import ca.uhn.hl7v2.model.Message;
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.parser.Parser;

public class ParseMessageExample {

    public static void main(String[] args) {
        String hl7Message =
            "MSH|^~\\&|SENDING_APP|SENDING_FACILITY|RECEIVING_APP|RECEIVING_FACILITY|20231117120000||ADT^A01|MSG00001|P|2.5\r" +
            "EVN|A01|20231117120000\r" +
            "PID|1||123456^^^HOSPITAL^MR||DOE^JOHN^A||19800115|M|||123 MAIN ST^^CITY^ST^12345^USA||(555)555-5555|||S||123456789\r" +
            "PV1|1|I|2000^2012^01||||004777^SMITH^JOHN^^^DR|||SUR||||ADM|A0|\r";

        try {
            HapiContext context = new DefaultHapiContext();
            Parser parser = context.getPipeParser();

            // Parse the message
            Message message = parser.parse(hl7Message);

            // Determine message type
            System.out.println("Message Type: " + message.getName());

            // Cast to specific message type
            if (message instanceof ADT_A01) {
                ADT_A01 adtMessage = (ADT_A01) message;

                // Access MSH segment
                MSH msh = adtMessage.getMSH();
                System.out.println("Sending Application: " +
                    msh.getSendingApplication().getNamespaceID().getValue());
                System.out.println("Sending Facility: " +
                    msh.getSendingFacility().getNamespaceID().getValue());
                System.out.println("Message Control ID: " +
                    msh.getMessageControlID().getValue());
                System.out.println("Message Date/Time: " +
                    msh.getDateTimeOfMessage().getTime().getValue());

                // Access PID segment
                PID pid = adtMessage.getPID();
                System.out.println("\nPatient Information:");
                System.out.println("Patient ID: " +
                    pid.getPatientIdentifierList(0).getIDNumber().getValue());
                System.out.println("Patient Name: " +
                    pid.getPatientName(0).getFamilyName().getSurname().getValue() + ", " +
                    pid.getPatientName(0).getGivenName().getValue());
                System.out.println("Date of Birth: " +
                    pid.getDateTimeOfBirth().getTime().getValue());
                System.out.println("Gender: " +
                    pid.getAdministrativeSex().getValue());
                System.out.println("Address: " +
                    pid.getPatientAddress(0).getStreetAddress()
                        .getStreetOrMailingAddress().getValue() + ", " +
                    pid.getPatientAddress(0).getCity().getValue() + ", " +
                    pid.getPatientAddress(0).getStateOrProvince().getValue() + " " +
                    pid.getPatientAddress(0).getZipOrPostalCode().getValue());
            }

            context.close();

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

Parsing Lab Results (ORU^R01)

Lab result messages contain nested groups that require careful navigation. The PATIENT_RESULT group contains patient information and one or more ORDER_OBSERVATION groups. Each ORDER_OBSERVATION contains an OBR segment describing the test panel and multiple OBSERVATION groups with individual OBX results. Use the getReps() methods to determine how many repetitions exist at each level before iterating through results.

import ca.uhn.hl7v2.DefaultHapiContext;
import ca.uhn.hl7v2.HapiContext;
import ca.uhn.hl7v2.model.Message;
import ca.uhn.hl7v2.model.v25.message.ORU_R01;
import ca.uhn.hl7v2.model.v25.segment.OBX;
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;

public class ParseLabResultsExample {

    public static void main(String[] args) {
        String hl7Message =
            "MSH|^~\\&|LAB|HOSPITAL|RECEIVER|HOSPITAL|20231117140000||ORU^R01|MSG00005|P|2.5\r" +
            "PID|1||123456^^^HOSPITAL^MR||DOE^JOHN^A||19800115|M\r" +
            "OBR|1|ORD123456||CBC^COMPLETE BLOOD COUNT^LOCAL|||20231117120000|||||||20231117140000|||004777^SMITH^JOHN^^^DR\r" +
            "OBX|1|NM|WBC^White Blood Count^LOCAL||7.5|10*3/uL|4.5-11.0|N|||F\r" +
            "OBX|2|NM|RBC^Red Blood Count^LOCAL||4.8|10*6/uL|4.5-5.5|N|||F\r" +
            "OBX|3|NM|HGB^Hemoglobin^LOCAL||14.5|g/dL|13.5-17.5|N|||F\r" +
            "OBX|4|NM|HCT^Hematocrit^LOCAL||43.2|%|41-53|N|||F\r";

        try {
            HapiContext context = new DefaultHapiContext();
            Parser parser = context.getPipeParser();

            Message message = parser.parse(hl7Message);

            if (message instanceof ORU_R01) {
                ORU_R01 oruMessage = (ORU_R01) message;

                System.out.println("Lab Results Message");
                System.out.println("===================\n");

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

                // Patient info
                String patientId = patientResult.getPATIENT().getPID()
                    .getPatientIdentifierList(0).getIDNumber().getValue();
                String patientName = patientResult.getPATIENT().getPID()
                    .getPatientName(0).getFamilyName().getSurname().getValue() + ", " +
                    patientResult.getPATIENT().getPID()
                    .getPatientName(0).getGivenName().getValue();

                System.out.println("Patient: " + patientName + " (ID: " + patientId + ")\n");

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

                // Test info
                String testCode = orderObs.getOBR().getUniversalServiceIdentifier()
                    .getIdentifier().getValue();
                String testName = orderObs.getOBR().getUniversalServiceIdentifier()
                    .getText().getValue();

                System.out.println("Test: " + testName + " (" + testCode + ")\n");
                System.out.println("Results:");
                System.out.println("--------");

                // Iterate through observations
                int numObs = orderObs.getOBSERVATIONReps();
                for (int i = 0; i < numObs; i++) {
                    OBX obx = orderObs.getOBSERVATION(i).getOBX();

                    String obsCode = obx.getObservationIdentifier()
                        .getIdentifier().getValue();
                    String obsName = obx.getObservationIdentifier()
                        .getText().getValue();
                    String obsValue = obx.getObservationValue(0).getData().toString();
                    String obsUnits = obx.getUnits().getIdentifier().getValue();
                    String refRange = obx.getReferencesRange().getValue();
                    String abnormalFlag = obx.getAbnormalFlags(0).getValue();

                    System.out.printf("%s (%s): %s %s [Reference: %s] %s\n",
                        obsName, obsCode, obsValue, obsUnits, refRange,
                        abnormalFlag.equals("N") ? "" : "ABNORMAL");
                }
            }

            context.close();

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

Generic Parsing (Version-Independent)

When you need to handle messages of any HL7 version without version-specific code, use the Terser utility class. Terser provides path-based access to message elements using string expressions like ”/.MSH-9-1” for the message type. This approach is especially useful when building generic message routers, transformers, or when the message version is unknown at compile time. The trade-off is less compile-time type safety compared to version-specific classes.

import ca.uhn.hl7v2.DefaultHapiContext;
import ca.uhn.hl7v2.HapiContext;
import ca.uhn.hl7v2.model.Message;
import ca.uhn.hl7v2.model.Segment;
import ca.uhn.hl7v2.model.Type;
import ca.uhn.hl7v2.parser.Parser;
import ca.uhn.hl7v2.util.Terser;

public class GenericParseExample {

    public static void main(String[] args) {
        String hl7Message =
            "MSH|^~\\&|SENDING_APP|SENDING_FACILITY|RECEIVING_APP|RECEIVING_FACILITY|20231117120000||ADT^A01|MSG00001|P|2.5\r" +
            "PID|1||123456^^^HOSPITAL^MR||DOE^JOHN^A||19800115|M\r";

        try {
            HapiContext context = new DefaultHapiContext();
            Parser parser = context.getGenericParser(); // Can parse any version

            Message message = parser.parse(hl7Message);

            // Use Terser for path-based access
            Terser terser = new Terser(message);

            System.out.println("Generic Parsing using Terser:");
            System.out.println("Message Type: " + terser.get("/.MSH-9-1"));
            System.out.println("Trigger Event: " + terser.get("/.MSH-9-2"));
            System.out.println("Sending Application: " + terser.get("/.MSH-3-1"));
            System.out.println("Patient ID: " + terser.get("/.PID-3-1"));
            System.out.println("Patient Family Name: " + terser.get("/.PID-5-1-1"));
            System.out.println("Patient Given Name: " + terser.get("/.PID-5-2"));
            System.out.println("Patient DOB: " + terser.get("/.PID-7-1"));
            System.out.println("Patient Gender: " + terser.get("/.PID-8"));

            context.close();

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

Quiz: Parsing HL7 V2 Messages

Question 1 of 5

What is the Terser utility used for in HAPI?