This is part of my HL7 article series. Before we get started on this tutorial, have a quick look at my earlier article titled “A Very Short Introduction to the HL7 2.x Standard” for a quick introduction to the HL7 2.x standard if you are just getting started on it. 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 Java alone. We then looked at a Java library called "HAPI" for building larger HL7 applications and explored how to create, transmit, receive as well as parse HL7 messages using this library. In this tutorial, we will look at another way to access HL7 message information using "tersers".
You can also find all the code demonstrated in the tutorial on GitHub here
“The troubleshooting guide contains the answer to every problem except yours” ~ Murphy’s Law
HAPI Tersers - An Overview
If you read my previous tutorial in this series titled “HL7 Programming using HAPI - Parsing Operations”, I explained in it that there were primarily two approaches for accessing as well as updating HL7 message data, namely via "Parsers" and "Tersers". We then reviewed what parsers were at a high level, and later explored some basic operations provided by the various HAPI parser classes in that tutorial. We will now look at a special class called Terser and the features it provides.
Being "terse" means being concise in the English language, and I suppose the authors of this library named these classes that way simply because they enable a concise way to access HL7 message data. In fact, tersers can be way more concise that parsers when retrieving as well as updating information and they use a Xpath-like syntax to refer to locations in the HL7 message. They eliminate the need for using the binding classes and the sometimes deeply nested methods to dig into the data hierarchy of a HL7 message. Tersers come in extremely handy when you interested in retrieving only specific portions of a HL7 message, and you are not particularly interested in rest of the message data during your message processing workflow. Using the code below, I will demonstrate how we can access as well as update message data using tersers.
Some Basic Operations using Tersers
Before I demostrate the terser functionality, I will create a small helper class to wrap the getter and setter behaviors around a terser instance so it is a bit easier to understand the operations in our code examples that follow underneath. You don't need to use a wrapper class like this in your application, but if you find yourself using terser expressions all over your code, streamlining atleast the core data access methods into a single location should make logging and debugging your applications a bit easier.
Now that we have our helper class, let us look at some basic operations that use the Terser class. Notice in the code below that I instantiate a terser by wrapping it aorund a message object. Although there are a lot of static methods on this class as well, I rarely use those. However those static methods do provide additional ways of getting at the data especially if you don't like using the terser expressions and like a more statically typed approach for data access. The operations shown below demonstrate various operations such as retrieving field, component and subcomponent-level data from a HL7 message using various terser expressions. Also, please keep the test HL7 file FileWithObservationResultMessage.txt handy as you read through the code below to make sense of what is going on. I have shared this file in my GitHub repository.
The console output from running our program is shown below. The terser expressions shown here should be easy to follow if you are familiar with other languages such X-Path which enable you to navigate through a object hierarchy. Let us look at some more advanced operations using tersers next.
More Advanced Operations using Tersers
In my opinion, tersers really shine when dealing with deeply nested HL7 messages such as orders and lab results. They are especially useful when needing to deal with HL7 segment groups both for get and set operations on the data in these messages. In HL7, a segment group is a collection of segments that always appear together in sequence. Not all message types contain segment groups. Even when they do, the segment groups can be optional, conditional and/or repeating. Some message types may even have multiple repeating groups of the same segement nested under different segments. See diagram below for an example of a HL7 vaccine update message where I have pointed out repeating OBX segment groups. This makes the process of parsing message data very complex especially if you are writing a custom parser from scratch. However, tersers in HAPI come to our rescue in these situations. They use expression syntaxes that follow the same object model names that define the segment groups in the message definition such as "PATIENT", "ORDER", "OBSERVATION", etc enabling you to traverse the segment hierarchy relatively easily. Tersers also allow us to set data easily.
Let us now look at a code example highligting some of these advanced capabilities. Again, keep the test file FileWithObservationResultMessage.txt that I have uploaded in my GitHub repository handy when you are going through the code below. I would also recommend that you experiment with any other long and complex HL7 message and explore the expression syntaxes even further. The investment will pay off especially if you are going to be dealing with complex HL7 message data.
The program console output from running our program is shown below. Notice that we have the ability to access any data in a message no matter how deeply it is nested. Tersers expressions are very powerful indeed, and can save us a lot of time and effort when needing to access message data in our HL7 applications.
That concludes our tutorial on using tersers provided by the HAPI HL7 library. There are a lot of other expressions that are available for you to try out, but hopefully I covered all the important ones that you will frequently use for accessing HL7 message data in your HAPI-enabled HL7 applications. You can review the HAPI documentation as well as source code for additional information. Something I would caution you here is to avoid the temptation to over use these in your application logic especially from a code readability and maintenance perspective. Troubleshooting terser expressions can sometimes be a problem if you are not careful about what you actually intended to do for a specific operation. Unit tests can be extremely useful in these situations since they can enable you to see whether the expression works exactly as you intended for a message being considered for your test. In the next tutorial in my HL7 article series, we will go back to the parser classes and see how they help us towards message validation as well as in testing message conformance profiles. See you then!