This is part of my HL7 article series. If you are just getting started with the HL7 2.x standard, please have a quick look at my earlier article titled “A Very Short Introduction to the HL7 2.x Standard”. One of my earlier tutorials in this series titled "HL7 Programming using Java" gave you a foundational understanding of how to build a simple HL7 message processing client and server using the Java programming language alone. We then looked at a Java library called "HAPI" for building larger HL7 applications and explored how to create, transmit and receive messages in my three articles namely "HL7 Programming using HAPI - Creating HL7 Messages", "HL7 Programming using HAPI - Sending HL7 Messages" and "HL7 Programming using HAPI - Receiving HL7 Messages". In this tutorial, we will explore the many ways in which you can extract HL7 message structures such as segment, fields, components, sub-components, etc using the HAPI framework.
Tools for Tutorial
- JDK 1.4 SDK or higher
- Eclipse or any other Java IDE (or even a text editor)
- Download HAPI compiled library from the HAPI project website
- View HAPI source code on their GitHub site here
- Download HAPI Test Panel (optional) from here
- You can also find all the code demonstrated in the tutorial on GitHub here
“So many books, so little time.” ~ Frank Zappa
HAPI Parsers - An Overview
HAPI Provides two mechanisms for accessing message information, and these are Parsers and Tersers. They both work quite differently from each other, and in this tutorial, we will look at the parser classes and their capabilities, and what you can use them for in your HL7-enabled applications. In a subsequent tutorial in this series, we will look at tersers as well.
In the field of computer science, the word "parser" often refers to the act of breaking down a piece of text such as a sentence, a string of words or any other linguistic construct into its smaller constituent parts to aid in either the structural or the semantic understanding of that material. This deconstructed information is often displayed in the form of a "parse tree" which refers to the act of displaying this information in a tree like structure. This enables us to see the big picture as to how the various parts are related to one another. The parser classes provided by HAPI contain many useful features including being able to convert from a message string to a message object (this is referred to as "parsing"), and also convert a HL7 message object back to a string format (referred to as "encoding" a message). With these parsers, you can translate the HL7 2.x information from as well as to various formats such as ER7 (sometimes also referred to as "normal encoding") and XML when serialization to a file, a database or transmission across the network is needed. These parsers also enable you to perform message validation and provide support for testing of conformance profiles in HL7. The list of capabilities of HAPI parsers is long. Because there are so many capabilities to cover, I will cover basic parsing operations in this tutorial and cover the more advanced parsing operations such as message validation and conformance profiles in the next tutorial in this series.
When you are dealing with message parsing, something you will begin to really appreciate about the HAPI library is the many strongly typed classes that it provides for dealing with nearly every HL7 message definition laid out in the various HL7 2.x standards. Since there are many versions of the HL7 2.x standard each of which consist of hundreds of message types and trigger events, hand coding parser classes can become extremely time consuming. HAPI authors overcame this problem through a rather ingenious way by auto-generating Java classes based on HL7 standard's official HL7 Microsoft Access Tables containing the HL7 2.x message definitions*. The Java classes generated by this approach help provide convenient binding interfaces that enable application programmers using the HAPI library to access as well as update message data pretty much along the same HL7 abstract message syntax model defined by the HL7 group. So, if you are looking to write code that follows the "HL7 2.x Information Model" more closely, then HAPI parsers can assist you with that. Let us look at some examples of using HAPI parsers now.
Basic Parser Operations
The code below demonstrates a few different operations you can perform using the parser classes. It shows how we can take a HL7 message string (in our case an ACK message response) and parse it into a HL7 message object using the PipeParser. It also shows you how when we have the information in a strongly typed HL7 message object, we can then easily access as well as update any data on the message such as a segment, field, etc. It then shows you how to use the DefaultXmlParser to encode the message object into XML format for display purposes.
Running the code above should result in the output similar to what is shown below. We are able to successfully parse the message string into a message object and able to access the message data easily. Here, I am displaying the message type, the message control id, the timestamp of when the message was origination as well as the sending facility for the message. We are also able to encode the message object into XML format for display. We are also able to display the message in an "abstract message syntax format" as seen below.
Parsing of Custom Message Models
An interesting and very useful feature that parsers in HAPI provide is in the area of HL7 message customization. Something you will run into occasionaly when dealing with HL7 2.x messaging systems is what is known as a "Z-segment". Occasionally a requirement emerges where communication of some special type of information is required which is not easily supported by the standard message definitions that are specified in the HL7 standard. In those situations, we can create a custom segment to help transmit this custom data. The standard convention is that all these custom segments begin with the letter Z. For instance, a "ZPV" segment may be used when customized patient visit information needs to be transmitted because there is some specialization information that needs to be recorded as part of the patient visit information. Because custom segments almost always start with the letter Z, they are also referred to as Z-segments. This feature helps provide quite a bit of flexibility to HL7 message communications. However, you need to be able to handle the extraction of the data from Z-segment when this is the case. Writing this parsing logic can be quite tricky as you need to handle the parsing of the entire message and the other message structures contained in it as well. HAPI library provides a flexible approach by allowing you to extend the standard parsing behaviour that you get out of the box by using the CustomModelClassFactory class which can be configured on the HAPI context when using the parser classes. Please see code examples below on how this is done using HAPI using a three-step process.
Step 1 of 3 - Create a Z-segment Class
First, we need to create an new HL7 segment definition that we will store our custom fields. We do this by extending the Abstract Segment class that comes with HAPI that provides a lot of pre-built functionality to manage segment data. This abstract class forces all implementing classes to initialize the various fields contained in them by invoking the init method from the constructor. We then utilize the add method that is already implemented in the abstract this class to define as well as initialize the fields in this segment. In the example below, I am defining two custom fields named "custom notes" and "custom description" that are both of ST data type, but you can implement fields using whatever data type that is supported in the HL7 standard. For the two custom fields, I am also specifying other characteristics such as whether the field is mandatory, the number of repetitions of this field as well as the field length. Lastly, note the method named createNewTypeWithoutReflection at the end of our custom segment class. As the name implies, this method helps indicate whether an instance of a field type should be creatied without using reflection. HAPI documentation suggests that we simply return null, and not to change this unless some very specialized behavior is required. So, reflection is used for message construction in our case.
Step 2 of 3 - Create a Specialized Class by Extending the ADT A01 Message Class
We need to define a new message type to carry the z-segment we just defined earlier as we need to inform the receiving system that this message is not a normal ADT A01 message. Here we are simply extending the ADT A01 message class provided by the HAPI library, and are adding additional behavior to support the new message segment. This approach enables us to build on the behavior already provided by the ADT A01 message class and also the other classes and interfaces that it extends or implements already. As you can see in the code below, we are specifying some additional behavior in the constructor of this new message type class, and are asking it to include new z-segment to its normal payload using the new z-segment class that we previously created.
Step 3 of 3 - Stitch The Behavior Together
In this last step, we bring all these specialized behavior together to help parse the new message type successfully. The HAPI context class used by the message parser uses a special custom model class factory which looks for our binding classes which we defined earlier at runtime. The package path of where these classes can be located is specified as a parameter. The pipe parser created from this context instance helps parse the custom message based on these binding classes. We can then extract the custom message segment including the custom message data from this message as shown in the code illustration below.
The results of running the main program above are shown below. By using an approach such as this, we reduce the amount of code we otherwise need to write and support in our custom HL7 applications. If you are interested in exploring this area further, you should look up the official HAPI source code and/or documentation for more information. While on the topic of Z-segments, I would ask you to be careful around the amount of customization you generally do as this tends to decrease the interoperability and supportability of the overall system especially if the remote systems are upgraded or when new systems come onboard. Besides, the standard already provides support for most scenarios anyway, and so you should always look at the HL7 official documentation before deciding to use Z-segments as there may be ways to do what you are looking for already.
That brings us to the end of yet another tutorial on using the HAPI HL7 library. We looked at some basic message parsing capabilities provided in the HAPI library. The Parser classes provided by this library help us to convert messages from and to various formats such as ER7, XML, etc. We also looked at how parsers enable us to process special Z-segments when site-specific customizations are in place. The parser classes provided by the HAPI library enable application programmers to concentrate more on writing the business logic of their applications, and they help reduce the time required to create and/or extend our HL7 2.x message-enabled applications especially when we need to process many different message types or trigger events. In the next tutorial in my HL7 article series, we will look at tersers quickly before moving on to review the more advanced features provided by the parser classes such as message validation and message conformance profiles. See you then!
* - Always consult the official HAPI and HL7 documentation for latest information as this may change or may have changed already.