HL7v2 Groovy Domain Specific Language
The Groovy-based HL7v2 DSL provides a unique programming interface for handling HL7v2 messages. Its API aligns very closely with natural language and the syntax of HL7v2 as often seen in specifications and requirements, so that translation from the language of the “HL7 world” into the language of the “developer’s world” becomes redundant.
How it works
The ipf-modules-hl7
library is basically a Groovy extension module that adds the required methods to the HAPI
model classes. These methods are used “under the hood” by the actual DSL.
IPF 2.x used a dedicated ipf-modules-hl7dsl
library and achieved the same goal by using adapters
wrapped around the HAPI model classes. With IPF 3.x, this library and all wrappers have been deprecated, and we
strongly recommend to migrate your applications. With IPF 3.4, the library has finally been removed.
Accessing HL7 messages
The DSL offers a position-based navigation of HL7 structures and fields. It’s all valid Groovy Syntax, accomplished by operator overloading and metaclass programming, so you don’t need a intermediate step that parses the expressions (see the Terser class in HAPI).
Accessing groups and segments
Groups and segments can be accessed by name like an object property.
import ca.uhn.hl7v2.model.Message
def message = parser.parse(msgTxt)
def msh = message.MSH
def group = message.PATIENT_RESULT
def pid = message.PATIENT_RESULT.PATIENT.PID
Details are described here.
Accessing fields and field values
Obtaining fields is similar to obtaining groups and segments except that fields are often referred to by number instead of by name. Fields are accessed like an array field. Components in a composite field are accessed like a two-dimensional array. Subcomponents are accessed like a three-dimensional array.
String messageType = message.MSH[9][1].value
String street = message.PATIENT_RESULT.PATIENT.PID[11][1][1].value
Details are described here.
Boolean conversion
All fields and structures implement an isEmpty()
method. For Groovy, this is also used
for boolean conversion, so that checks for emptiness become even more concise:
if (message.MSH[9][3]) {
println('Message structure is present')
}
Repetitions
Groups, segments and fields may be repeatable. Parentheses like with regular method calls are used in order to obtain a certain element of a repeating structure.
def group = message.PATIENT_RESULT(0).PATIENT
def nk1 = group.NK1(1)
Details are described here.
Smart navigation
Accessing HL7 messages as described above usually requires knowledge about the specified message structure, which is often not visible by looking at the printed message. To make things worse, the internal structure changes between HL7 versions. In more recent versions, primitive types are sometimes replaced with composite types having the so far used primitive as first component. This appears to be backwards compatible on printed messages, but requires different DSL expressions when obtaining field values.
Smart navigation resolves these problems by assuming reasonable defaults when repetitions or component operators are omitted
assert group.NK1(0)[2][1][1].value == group.NK1[2].value
Details are described here.
Iterative functions
As HL7 messages are compound structures, it should be possible to traverse them. Thus, the HL7 DSL implements iterators for HL7 messages and groups. Due to their nested structures, iteration is implemented as a depth first traversal over all non-empty substructures, i.e. non-empty groups and segments.
import ca.uhn.hl7v2.model.Message
boolean hasGroups = message.any { it instanceof Group }
def allStructureNames = message*.name
Details are described here.
Creating HL7 messages (and their internal structures)
New messages can be created from scratch by just specifying HL7 event type, trigger event and version, i.e. it is not required to know about the object type to be instantiated. The message header fields are populated with the event type, trigger event, version, the current time as message date, and the common separators.
Just as creating a message, segments and fields can be created without actually knowing their respective type.
Details are described here.
Manipulating HL7 messages
Message manipulation is as straightforward as read access. You navigate to a segment or field and assign it a new object or String value.
Details are described here.
Parsing and Rendering HL7 messages
IPF does not change or extend HAPI on how to parse or render HL7 message.