HL7Kit is a strict HL7 v2 parser for .NET that converts pipe-delimited HL7 messages into strongly-typed C# POCOs.
It is designed for modern integration work where you need:
- Strongly typed access to common segments
- Strict malformed-message detection with clear exceptions
- Proper handling of HL7 delimiters, escapes, repeats, and character sets
- Safe access to custom and unknown segments without losing data
dotnet add package HL7Kit --version 0.1.0Or with PackageReference:
<ItemGroup>
<PackageReference Include="HL7Kit" Version="0.1.0" />
</ItemGroup>FHIR converter package:
dotnet add package HL7Kit.Fhir --version 1.0.0Or with PackageReference:
<ItemGroup>
<PackageReference Include="HL7Kit.Fhir" Version="1.0.0" />
</ItemGroup>using HL7Kit;
var raw = "MSH|^~\\&|ADTAPP|HOSP|EHR|FAC|20260430103045||ADT^A01^ADT_A01|MSG00001|P|2.5\r"
+ "PID|1||123456^^^HOSP^MR||Doe^Jane^Alice^^Ms\r"
+ "OBX|1|ST|94531-1^COVID result^LN||Negative|||||F\r";
Hl7Message message = Hl7Message.Parse(raw);
string? sendingApp = message.Msh.SendingApplication;
string? familyName = message.Pid?.PatientName.FamilyName;
string? obxValue = message.Obx.FirstOrDefault()?.ObservationValue;HL7Kit.Fhir converts parsed HL7Kit HL7 v2 message objects into Firely .NET SDK FHIR R4 resources.
PID->PatientPV1->EncounterOBX->ObservationNK1->RelatedPersonAL1->AllergyIntoleranceDG1->ConditionToBundle()-> transactionBundlewith all successfully converted resources
using HL7Kit;
using HL7Kit.Fhir;
using Hl7.Fhir.Model;
Hl7Message message = Hl7Message.Parse(raw);
Patient patient = Hl7FhirConverter.ToPatient(message);
Encounter encounter = Hl7FhirConverter.ToEncounter(message);
Observation observation = Hl7FhirConverter.ToObservation(message);
Bundle bundle = Hl7FhirConverter.ToBundle(message);If you already know libraries like NHapi, HL7Kit is intentionally opinionated in a few areas:
- Strict-first parsing: malformed input throws clear
Hl7ParseExceptionerrors instead of silently tolerating questionable structure. - Modern .NET ergonomics: nullable-aware POCOs, focused value types (
PersonName,CodifiedValue,Hl7DateTime), and straightforward APIs. - Delimiter/escape correctness per message: respects dynamic
MSH.1/MSH.2, escapes, repeats, and charset behavior. - No data loss for unsupported segments: captures both
Zsegments and other unknown segments so integrations stay forward-compatible. - Incremental typed coverage model: strongly typed where implemented, practical string fallback where not yet implemented.
Top-level parsed message includes typed access to:
MSH,PID,PV1,OBR,OBX,NK1EVN,AL1,DG1,IN1,MSA,ERR,NTE,ORC,SCH
Hl7Message.Parse(...) throws Hl7ParseException for malformed input, including:
- Missing/invalid
MSH - Invalid segment IDs
- Invalid date/time formats
- Invalid or unsupported escape sequences
- Invalid repeat usage in fields expected to be single-valued
Per-message delimiter set is read from MSH.1 and MSH.2.
Supported:
- Field delimiter (usually
|) - Component delimiter (usually
^) - Repetition delimiter (usually
~) - Escape delimiter (usually
\\) - Subcomponent delimiter (usually
&)
Supported escapes:
\F\,\S\,\R\,\E\,\T\\.br\\X...\hex-encoded bytes\Cxxxx\single-byte character set switches (C2842,C2D41)
Character set resolution from MSH.18 includes:
- ASCII variants
- ISO-8859-1 variants
- UTF-8 variants (
UTF-8,UTF8,UNICODE UTF-8,UNICODE UTF8)
- HL7 null literal
""is interpreted asnull - Empty field is
null - Leading/trailing spaces in field values are preserved
- Whitespace-only field values are preserved
Custom Zxx segments are captured in message.ZSegments.
if (message.ZSegments.TryGetValue("ZPI", out var zpiSegments))
{
var first = zpiSegments[0];
string? rawField1 = first.GetField(1);
string? component2 = first.GetComponent(1, 2);
string rawLine = first.Raw;
}Unknown non-Z segments are captured in message.UnknownSegments.
if (message.UnknownSegments.TryGetValue("ABC", out var abcSegments))
{
string? field1 = abcSegments[0].GetField(1);
}HL7Kit includes reusable value types:
PersonNameCodifiedValueHl7DateTime
Hl7DateTime supports:
YYYYYYYYMMYYYYMMDDYYYYMMDDHHmmYYYYMMDDHHmmss
For supported segments, properties are mapped to HL7 field positions with XML docs.
- Fields with implemented parsing use strong types where applicable
- Fields not fully implemented are currently represented as
string?with TODO markers
- .NET 10 (
net10.0)
MIT. See LICENSE.