HL7-V2
3.0.0
See the version list below for details.
dotnet add package HL7-V2 --version 3.0.0
NuGet\Install-Package HL7-V2 -Version 3.0.0
<PackageReference Include="HL7-V2" Version="3.0.0" />
paket add HL7-V2 --version 3.0.0
#r "nuget: HL7-V2, 3.0.0"
// Install HL7-V2 as a Cake Addin #addin nuget:?package=HL7-V2&version=3.0.0 // Install HL7-V2 as a Cake Tool #tool nuget:?package=HL7-V2&version=3.0.0
HL7-V2
This is a lightweight library for building and parsing HL7 2.x messages, for .NET Standard, .NET Core, and .NET 5+. It is not tied to any particular version of HL7 nor validates against one.
Usage and compatibility
This library is distributed via nuget and targets two frameworks:
- .NET Standard 2.0 for maximum compability, covering more than 40 .NET frameworks
- .NET 8.0 for better performance under the new Microsoft's cross-platform framework
For using the classes and methods mentioned below, declare de following namespace:
using Efferent.HL7.V2;
Object construction
Create a Message object and pass a raw HL7 message in text format
Message message = new Message(strMsg);
// Parse this message
bool isParsed = false;
try
{
isParsed = message.ParseMessage();
}
catch(Exception ex)
{
// Handle the exception
}
Adding a message header
For adding a header segment to a new message object, use the AddSegmentMSH()
method, after constructing an empty message:
message.AddSegmentMSH(sendingApplication, sendingFacility,
receivingApplication, receivingFacility,
security, messageType,
messageControlId, processingId, version);
⚠️ Notice that every HL7 message needs a header segment to be considered valid
Message extraction
If the HL7 message is coming from a MLLP connection (see the official documentation), the message needs to be cleared from the MLLP prefixes and suffixes. Also, consider there can be more than one message in a single MLLP frame.
For this purpose, there is an ExtractMessages()
method, to be used as follows:
// extract the messages from a buffer containing a MLLP frame
var messages = MessageHelper.ExtractMessages(buffer);
// construct and process each message
foreach (var strMsg in messages)
{
Message message = new Message(strMsg);
message.ParseMessage(); // Required by most operations
// do something with the message object
}
Bypass Validation
Message message = new Message(strMsg)
message.ParseMessage(true);
Accessing Segments
Get list of all segments
List<Segment> segList = message.Segments();
Get List of list of repeated segments by name
For example if there are multiple IN1 segments
List<Segment> IN1List = message.Segments("IN1");
Access a particular occurrence from multiple IN1s providing the index
Note index 1 will return the 2nd element from list
Segment IN1_2 = message.Segments("IN1")[1];
Get count of IN1s
int countIN1 = message.Segments("IN1").Count;
Access first occurrence of any segment
Segment IN1 = message.DefaultSegment("IN1");
// OR
Segment IN1 = message.Segments("IN1")[0];
Accessing Fields
Access field values
string SendingFacility = message.GetValue("MSH.4");
// OR
string SendingFacility = message.DefaultSegment("MSH").Fields(4).Value;
// OR
string SendingFacility = message.Segments("MSH")[0].Fields(4).Value;
Segment ocurrences
string ContactPhone = message.GetValue("NK1(2).5"); // Second occurrence of NK1
Check if field is componentized
bool isComponentized = message.Segments("PID")[0].Fields(5).IsComponentized;
// OR
bool isComponentized = message.IsComponentized("PID.5");
Check if field has repetitions
bool isRepeated = message.Segments("PID")[0].Fields(3).HasRepetitions;
// OR
bool isRepeated = message.HasRepetitions("PID.3");
Adding repeating field
var enc = new HL7Encoding();
Segment PID = new Segment("PID", enc);
Field f = new Field(enc);
f.HasRepetitions = true;
// Adding field f1 to f
Field f1 = new Field("A", enc);
f.AddRepeatingField(f1);
// Adding field f2 to f
Field f2 = new Field("B", enc);
f.AddRepeatingField(f2);
Get list of repeated fields
List<Field> repList = message.Segments("PID")[0].Fields(3).Repetitions();
Get particular repetition i.e. 2nd repetition of PID.3
Field PID3_R2 = message.GetValue("PID.3[2]");
// OR
Field PID3_R2 = message.GetValue("PID.3(2)");
Update value of any field i.e. to update PV1.2 – patient class
message.SetValue("PV1.2", "I");
// OR
message.Segments("PV1")[0].Fields(2).Value = "I";
Access some of the required MSH fields with properties
string version = message.Version;
string msgControlID = message.MessageControlID;
string messageStructure = message.MessageStructure;
Generate ACKs
To generate an ACK message
Message ack = message.GetACK();
To generate negative ACK (NACK) message with error message
Message nack = message.GetNACK("AR", "Invalid Processing ID");
It may be required to change the application and facility fields
Message ack = message.GetACK();
ack.SetValue("MSH.3", appName);
ack.SetValue("MSH.4", facility);
⚠️ Take into account that a message shall be previously parsed before attempting to generate an ACK or NACK message.
Accessing Components
Access particular component i.e. PID.5.1 – Patient Family Name
string PatName1 = message.GetValue("PID.5.1");
// OR
string PatName1 = message.Segments("PID")[0].Fields(5).Components(1).Value;
Check if component is sub componentized
bool isSubComponentized = message.Segments("PV1")[0].Fields(7).Components(1).IsSubComponentized;
// OR
bool isSubComponentized = message.IsSubComponentized("PV1.7.1");
Update value of any component
message.Segments("PID")[0].Fields(5).Components(1).Value = "Jayant";
// OR
message.SetValue("PID.5.1", "Jayant");
Adding new Segment
// Create a Segment with name ZIB
Segment newSeg = new Segment("ZIB");
// Create Field ZIB_1
Field ZIB_1 = new Field("ZIB1");
// Create Field ZIB_5
Field ZIB_5 = new Field("ZIB5");
// Create Component ZIB.5.2
Component com1 = new Component("ZIB.5.2");
// Add Component ZIB.5.2 to Field ZIB_5
// 2nd parameter here specifies the component position, for inserting segment on particular position
// If we don’t provide 2nd parameter, component will be inserted to next position (if field has 2 components this will be 3rd,
// If field is empty this will be 1st component
ZIB_5.AddNewComponent(com1, 2);
// Add Field ZIB_1 to segment ZIB, this will add a new filed to next field location, in this case first field
newSeg.AddNewField(ZIB_1);
// Add Field ZIB_5 to segment ZIB, this will add a new filed as 5th field of segment
newSeg.AddNewField(ZIB_5, 5);
// Add segment ZIB to message
bool success = message.AddNewSegment(newSeg);
New Segment would look like this:
ZIB|ZIB1||||ZIB5^ZIB.5.2
After evaluated and modified required values, the message can be obtained again in text format
string strUpdatedMsg = message.SerializeMessage();
Remove Trailing Components
var message = new Message();
// create ORC segment
var orcSegment = new Segment("ORC", new HL7Encoding());
// add fields
for (int eachField = 1; eachField <= 12; eachField++)
{
orcSegment.AddEmptyField();
}
// add components to field 12
for (int eachField = 1; eachField < 8; eachField++)
{
orcSegment.Fields(12).AddNewComponent(new Component(new HL7Encoding()));
}
// add values to components
orcSegment.Fields(12).Components(1).Value = "should not be removed";
orcSegment.Fields(12).Components(2).Value = "should not be removed";
orcSegment.Fields(12).Components(3).Value = "should not be removed";
orcSegment.Fields(12).Components(4).Value = ""; // should not be removed because in between valid values
orcSegment.Fields(12).Components(5).Value = "should not be removed";
orcSegment.Fields(12).Components(6).Value = ""; // should be removed because trailing
orcSegment.Fields(12).Components(7).Value = ""; // should be removed because trailing
orcSegment.Fields(12).Components(8).Value = ""; // should be removed because trailing
orcSegment.Fields(12).RemoveEmptyTrailingComponents();
message.AddNewSegment(orcSegment);
string serializedMessage = message.SerializeMessage(false);
Remove a Segment
Segments are removed individually, including the case where there are repeated segments with the same name
// Remove the first segment with name NK1
bool success = message.RemoveSegment("NK1")
// Remove the second segment with name NK1
bool success = message.RemoveSegment("NK1", 1)
Encoded segments
Some contents may contain forbidden characters like pipes and ampersands. Whenever there is a possibility of having those characters, the content shall be encoded before calling the 'AddNew' methods, like in the following code:
var obx = new Segment("OBX", new HL7Encoding());
// Not encoded. Will be split into parts.
obx.AddNewField("70030^Radiologic Exam, Eye, Detection, FB^CDIRadCodes");
// Encoded. Won't be parsed nor split.
obx.AddNewField(obx.Encoding.Encode("domain.com/resource.html?Action=1&ID=2"));
Copying a segment
The DeepCopy method allows to perform a clone of a segment when building new messages. Countersense, if a segment is referenced directly when adding segments to a message, a change in the segment will affect both the origin and new messages.
Segment pid = ormMessage.DefaultSegment("PID").DeepCopy();
oru.AddNewSegment(pid);
Null elements
Null elements (fields, components or subcomponents), also referred to as Present But Null, are expressed in HL7 messages as double quotes, like (see last field):
EVN|A04|20110613083617||""
Whenever requested individually, those elements are returned as null
, rather than double quotes:
var expectEmpty = evn.Fields(3).Value; // Will return an empty string
var expectNull = evn.Fields(4).Value; // Will return null
If this behavior is not desirable, it can be disabled by setting Encoding.PresentButNull
to null
before parsing:
var message = new Message(msg);
message.Encoding.PresentButNull = null;
message.ParseMessage();
Date Handling
A couple of date handling methods have been added, for parsing elements containing valid date/times, including time zones, as described in the HL7 standard. Examples:
// With time zone
string value1 = "20151231234500.1234+2358";
TimeSpan offset;
DateTime? dt1 = MessageHelper.ParseDateTime(value1, out offset);
// Date/time only
string value2 = "20151231234500";
DateTime? dt2 = MessageHelper.ParseDateTime(value2);
ParseDateTime
will catch exceptions by default and return null
in case of invalid dates. For preventing this mechanism, add an extra argument as true
, like:
try
{
var dt1 = MessageHelper.ParseDateTime(value1, out TimeSpan offse, true);
var dt2 = MessageHelper.ParseDateTime(value2, true);
}
catch
{
// do something here
}
Credits
This library took Jayant Singh's HL7 parser (2013) as a foundation. Since then, it has been modified fundamentally, with respect to features, code quality, bugs and typos. For more information about the original implementation read:
The field encoding and decoding methods have been based on https://github.com/elomagic/hl7inspector
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. |
.NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
.NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
.NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
MonoAndroid | monoandroid was computed. |
MonoMac | monomac was computed. |
MonoTouch | monotouch was computed. |
Tizen | tizen40 was computed. tizen60 was computed. |
Xamarin.iOS | xamarinios was computed. |
Xamarin.Mac | xamarinmac was computed. |
Xamarin.TVOS | xamarintvos was computed. |
Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- No dependencies.
-
net8.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.