Advanced Topics

Section 12 of 14
86% complete

Working with Z-Segments (Custom Segments)

Z-segments are custom segments that extend the HL7 standard to meet organization-specific needs. By convention, any segment starting with ā€œZā€ is considered locally defined and will not conflict with official HL7 segments. Common uses include adding billing codes, custom patient attributes, or facility-specific workflow data. HAPI treats Z-segments as generic segments, so you access fields by index rather than through typed accessors.

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 ZSegmentExample {

    public static void main(String[] args) {
        // Message with custom Z-segment
        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" +
            "ZPD|PREMIUM|VIP_PATIENT|PRIVATE_ROOM_PREFERRED\r"; // Custom Z-segment

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

            Message message = parser.parse(hl7Message);

            // Access Z-segment using Terser
            Terser terser = new Terser(message);

            System.out.println("Accessing Z-Segment (ZPD):");

            // Get the ZPD segment
            Segment zpd = (Segment) message.get("ZPD");

            if (zpd != null) {
                System.out.println("ZPD Segment found");

                // Access fields in Z-segment
                Type field1 = zpd.getField(1, 0);
                Type field2 = zpd.getField(2, 0);
                Type field3 = zpd.getField(3, 0);

                System.out.println("ZPD-1 (Patient Category): " + field1.encode());
                System.out.println("ZPD-2 (VIP Status): " + field2.encode());
                System.out.println("ZPD-3 (Room Preference): " + field3.encode());
            }

            context.close();

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

Message Enrichment

Message enrichment adds data to an existing message before forwarding it to downstream systems. This is common in integration scenarios where the original message lacks information needed by the receiver. For example, an ADT message from registration might need next-of-kin information added from a separate system before being sent to the nursing unit. HAPI makes enrichment straightforward by allowing you to access and modify segments on a parsed message object.

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.NK1;
import ca.uhn.hl7v2.parser.Parser;

public class MessageEnrichmentExample {

    public static void main(String[] args) {
        String basicMessage =
            "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.getPipeParser();

            ADT_A01 adtMessage = (ADT_A01) parser.parse(basicMessage);

            // Add next of kin information
            NK1 nk1 = adtMessage.getNK1();
            nk1.getSetIDNK1().setValue("1");

            // Name of next of kin
            nk1.getNKName(0).getFamilyName().getSurname().setValue("DOE");
            nk1.getNKName(0).getGivenName().setValue("JANE");

            // Relationship
            nk1.getRelationship().getIdentifier().setValue("SPO");
            nk1.getRelationship().getText().setValue("SPOUSE");

            // Address
            nk1.getAddress(0).getStreetAddress().getStreetOrMailingAddress()
                .setValue("123 MAIN ST");
            nk1.getAddress(0).getCity().setValue("CITY");
            nk1.getAddress(0).getStateOrProvince().setValue("ST");
            nk1.getAddress(0).getZipOrPostalCode().setValue("12345");

            // Phone
            nk1.getPhoneNumber(0).getTelephoneNumber().setValue("5555555556");

            // Encode enriched message
            String enrichedMessage = parser.encode(adtMessage);

            System.out.println("Original Message:");
            System.out.println(basicMessage);
            System.out.println("\nEnriched Message:");
            System.out.println(enrichedMessage);

            context.close();

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

Message Transformation (Version Conversion)

Converting messages between HL7 versions is sometimes necessary when integrating systems that use different versions. HAPI does not provide automatic version conversion because field definitions and semantics can differ. Instead, use Terser to copy field values between version-specific message objects. The Terser path syntax works across versions, making it ideal for transformation scenarios. Be aware that some data may require mapping or reformatting when moving between versions.

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

public class VersionConversionExample {

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

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

            // Parse V2.3 message
            Message v23Msg = parser.parse(v23Message);
            System.out.println("Original Version: " + v23Msg.getVersion());

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

            // Copy data using Terser
            Terser sourceTerser = new Terser(v23Msg);
            Terser targetTerser = new Terser(v25Msg);

            // Copy common fields
            targetTerser.set("/.MSH-3-1", sourceTerser.get("/.MSH-3-1"));
            targetTerser.set("/.MSH-4-1", sourceTerser.get("/.MSH-4-1"));
            targetTerser.set("/.MSH-5-1", sourceTerser.get("/.MSH-5-1"));
            targetTerser.set("/.MSH-6-1", sourceTerser.get("/.MSH-6-1"));
            targetTerser.set("/.MSH-10", sourceTerser.get("/.MSH-10"));

            targetTerser.set("/.PID-3-1", sourceTerser.get("/.PID-3-1"));
            targetTerser.set("/.PID-5-1-1", sourceTerser.get("/.PID-5-1"));
            targetTerser.set("/.PID-5-2", sourceTerser.get("/.PID-5-2"));
            targetTerser.set("/.PID-7-1", sourceTerser.get("/.PID-7"));
            targetTerser.set("/.PID-8", sourceTerser.get("/.PID-8"));

            // Encode V2.5 message
            String v25Message = parser.encode(v25Msg);

            System.out.println("\nV2.3 Message:");
            System.out.println(v23Message);
            System.out.println("\nConverted to V2.5:");
            System.out.println(v25Message);

            context.close();

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

Batch Message Processing

Batch processing handles multiple messages efficiently, which is useful for file-based interfaces, bulk migrations, or processing message backlogs. When processing batches, track success and failure counts to report processing results. Java streams with parallel execution can significantly improve throughput for CPU-bound parsing operations. However, ensure thread safety when writing results to shared resources.

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

import java.util.ArrayList;
import java.util.List;

public class BatchProcessingExample {

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

            // Simulate reading multiple messages
            List<String> messages = new ArrayList<>();
            messages.add(
                "MSH|^~\\&|APP1|FAC1|APP2|FAC2|20231117120000||ADT^A01|MSG001|P|2.5\r" +
                "PID|1||P001^^^HOSPITAL^MR||DOE^JOHN^A||19800115|M\r"
            );
            messages.add(
                "MSH|^~\\&|APP1|FAC1|APP2|FAC2|20231117120100||ADT^A01|MSG002|P|2.5\r" +
                "PID|1||P002^^^HOSPITAL^MR||SMITH^JANE^B||19850620|F\r"
            );
            messages.add(
                "MSH|^~\\&|APP1|FAC1|APP2|FAC2|20231117120200||ADT^A01|MSG003|P|2.5\r" +
                "PID|1||P003^^^HOSPITAL^MR||JONES^BOB^C||19700310|M\r"
            );

            System.out.println("Processing batch of " + messages.size() + " messages\n");

            int successCount = 0;
            int errorCount = 0;

            for (String hl7String : messages) {
                try {
                    Message message = parser.parse(hl7String);

                    ca.uhn.hl7v2.util.Terser terser =
                        new ca.uhn.hl7v2.util.Terser(message);

                    String msgControlId = terser.get("/.MSH-10");
                    String patientId = terser.get("/.PID-3-1");
                    String patientName = terser.get("/.PID-5-1-1") + ", " +
                                        terser.get("/.PID-5-2");

                    System.out.println("Processed Message: " + msgControlId);
                    System.out.println("  Patient: " + patientName + " (ID: " + patientId + ")");

                    successCount++;

                } catch (Exception e) {
                    System.err.println("Error processing message: " + e.getMessage());
                    errorCount++;
                }
            }

            System.out.println("\nBatch Processing Summary:");
            System.out.println("Total Messages: " + messages.size());
            System.out.println("Successful: " + successCount);
            System.out.println("Errors: " + errorCount);

            context.close();

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

Quiz: Advanced Topics

Question 1 of 5

What naming convention must custom segments follow in HL7 V2?