KeryxPars.Core
0.5.2-beta
dotnet add package KeryxPars.Core --version 0.5.2-beta
NuGet\Install-Package KeryxPars.Core -Version 0.5.2-beta
<PackageReference Include="KeryxPars.Core" Version="0.5.2-beta" />
<PackageVersion Include="KeryxPars.Core" Version="0.5.2-beta" />
<PackageReference Include="KeryxPars.Core" />
paket add KeryxPars.Core --version 0.5.2-beta
#r "nuget: KeryxPars.Core, 0.5.2-beta"
#:package KeryxPars.Core@0.5.2-beta
#addin nuget:?package=KeryxPars.Core&version=0.5.2-beta&prerelease
#tool nuget:?package=KeryxPars.Core&version=0.5.2-beta&prerelease
KeryxPars
Keryx (κῆρυξ) - Greek for "herald" or "messenger" | Pars - Latin for "part"
A high-performance, modern .NET parser built for healthcare interoperability. KeryxPars delivers enterprise-grade speed and memory efficiency with zero custom converter code needed.
🎯 Why KeryxPars?
Healthcare software shouldn't be slow and proprietary. KeryxPars provides open, performant, and extensible tools that enable innovative healthcare integrations.
The Problem
Current healthcare interoperability tools are:
- ❌ Slow (50-100μs+ parse times)
- ❌ Inefficient (GC pressure, allocations)
- ❌ Complex (thousands of lines for custom converters)
- ❌ Limited (incomplete segment support)
The Solution
KeryxPars delivers:
- ✅ 5-10x Faster: <10μs parse times with zero-allocation
- ✅ Zero Boilerplate: Generic converters eliminate custom code
- ✅ Production Ready: 50 segments covering enterprise workflows
- ✅ Type Safe: Compile-time safety with strongly-typed segments
✨ Key Features
High Performance
- Zero-allocation parsing using
Span<char>andref struct - No reflection in hot paths - direct property access
- Lazy initialization - only allocate what you use
- Frozen collections for O(1) segment lookup
- Benchmarked: 5-10x faster than popular alternatives
Developer Experience
- Type-safe HL7 data types (ST, NM, ID, CE, XPN, XCN, etc.)
- Generic converters - zero custom converter code
- Result<T, E> pattern for predictable error handling
- Extensive XML documentation on all segments and fields
- Consistent patterns across all 50 segments
Comprehensive Coverage
- 50 segments supporting critical healthcare workflows
- 12+ message types fully supported
- 1,100+ fields strongly-typed
- Complete workflows: ADT, Pharmacy, Lab, Scheduling, Financial, Query
Enterprise Ready
- Production-tested architecture
- Thread-safe parsing and serialization
- Apache 2.0 License for commercial use
- 42% HL7 v2.5 coverage and growing
🚀 Quick Start
Installation
# Using .NET CLI
dotnet add package KeryxPars.HL7
# Using Package Manager
Install-Package KeryxPars.HL7
Parse an ADT Message
using KeryxPars.HL7.Serialization;
var hl7Text = @"MSH|^~\&|SENDING_APP|SENDING_FAC|RECEIVING_APP|RECEIVING_FAC|20230101120000||ADT^A01|MSG001|P|2.5||
EVN|A01|20230101120000||
PID|1||123456||DOE^JOHN^A||19800101|M|||123 MAIN ST^^CITY^ST^12345|||||||
PV1|1|I|WARD^ROOM^BED||||ATTEND_DOC^ATTENDING^A|||||||||||V12345|||||||||||||||||||||||||20230101120000|";
var result = HL7Serializer.Deserialize(hl7Text);
if (result.IsSuccess)
{
var message = result.Value;
// Strongly-typed access with full IntelliSense support
var patientName = message.Pid.PatientName[0];
Console.WriteLine($"Patient: {patientName.FamilyName}, {patientName.GivenName}");
Console.WriteLine($"MRN: {message.Pid.PatientIdentifierList[0].IDNumber}");
Console.WriteLine($"DOB: {message.Pid.DateTimeofBirth.Value}");
Console.WriteLine($"Visit Number: {message.Pv1.VisitNumber.IDNumber}");
}
Parse Pharmacy Orders
var orderMessage = @"MSH|^~\&|PHARMACY|HOSPITAL|RX_SYSTEM|HOSPITAL|20230101120000||RDE^O11|ORD001|P|2.5||
PID|1||987654||SMITH^JANE^M||19900215|F|||
ORC|NW|ORD123456|FIL789012||||^^^20230101||
RXE|^^^20230101^20230201|00378-1805-10^Metformin 500mg^NDC|500||MG||||||10|||
RXR|PO^Oral^HL70162|||
TQ1|1||BID^Twice daily|||20230101120000|20230201120000|";
var result = HL7Serializer.Deserialize(orderMessage, SerializerOptions.ForMedicationOrders());
if (result.IsSuccess)
{
foreach (var order in result.Value.Orders)
{
Console.WriteLine($"Order Control: {order.Orc?.OrderControl.Value}");
Console.WriteLine($"Medication: {order.Rxe?.GiveCode.Text}");
Console.WriteLine($"Dose: {order.Rxe?.GiveAmountMinimum.Value} {order.Rxe?.GiveUnits.Text}");
Console.WriteLine($"Route: {order.RXR.FirstOrDefault()?.Route.Text}");
Console.WriteLine($"Frequency: {order.TQ1.FirstOrDefault()?.Quantity.Value}");
}
}
Create and Serialize Messages
using KeryxPars.HL7.Definitions;
using KeryxPars.HL7.Segments;
using KeryxPars.HL7.DataTypes.Primitive;
using KeryxPars.HL7.DataTypes.Composite;
var message = new HL7Message
{
Msh = new MSH
{
SendingApplication = new HD { NamespaceID = "MY_APP" },
SendingFacility = new HD { NamespaceID = "MY_FACILITY" },
ReceivingApplication = new HD { NamespaceID = "TARGET_APP" },
ReceivingFacility = new HD { NamespaceID = "TARGET_FACILITY" },
MessageType = new ST("ADT^A01"),
MessageControlID = new ST("MSG12345"),
ProcessingID = new ST("P"),
VersionID = new ST("2.5")
},
Pid = new PID
{
PatientIdentifierList = [new CX { IDNumber = new ST("123456") }],
PatientName = [new XPN
{
FamilyName = new FN { Surname = new ST("DOE") },
GivenName = new ST("JOHN")
}],
DateTimeofBirth = new DT("19800101"),
AdministrativeSex = new IS("M")
}
};
var result = HL7Serializer.Serialize(message);
Console.WriteLine(result.Value); // HL7 formatted message
🏗️ Architecture
Zero-Allocation Parsing
KeryxPars uses modern .NET features for maximum performance:
// Span<char> based parsing - zero allocations
public ref struct SegmentReader
{
private ReadOnlySpan<char> _segment;
public bool TryReadField(char delimiter, out ReadOnlySpan<char> field)
{
// Direct span slicing - no string allocations
// Typical parse: <10μs for complex messages
}
}
Generic Segment Converter Pattern
Zero custom converter code needed - all 50 segments use the same generic converter:
// One converter works for ALL segments!
public class GenericSegmentConverter<TSegment> : ISegmentConverter
where TSegment : ISegment, new()
{
public Result<ISegment, HL7Error> Read(ref SegmentReader reader, DeserializationContext context)
{
var segment = new TSegment();
int fieldIndex = 1;
while (reader.TryReadField('|', out var fieldValue))
{
segment.SetValue(fieldValue.ToString(), fieldIndex++);
}
return Result<ISegment, HL7Error>.Ok(segment);
}
}
// Register once, use everywhere
registry.Register(new GenericSegmentConverter<PID>());
registry.Register(new GenericSegmentConverter<OBX>());
// ... all 50 segments work the same way!
Strongly-Typed Data Types
All HL7 data types implemented with full spec compliance:
Primitive Types:
ST- StringTX- TextFT- Formatted TextID- Coded Value (HL7 table)IS- Coded Value (user table)NM- NumericSI- Sequence IDDT- DateTM- TimeDTM- DateTime
Composite Types:
CE- Coded ElementCWE- Coded with ExceptionsCX- Extended Composite IDXPN- Extended Person NameXCN- Extended Composite ID Number and NameXAD- Extended AddressXTN- Extended Telecom NumberXON- Extended Composite Organization NameEI- Entity IdentifierPL- Person LocationCQ- Composite Quantity with UnitsHD- Hierarchic DesignatorDR- Date Range- And more...
📊 Supported Segments (50 Total)
Header Segments (4)
- MSH - Message Header
- MSA - Message Acknowledgement
- ERR - Error Information
- EVN - Event Type
Patient Information (6)
- PID - Patient Identification (41 fields)
- PD1 - Additional Demographics (23 fields)
- NK1 - Next of Kin (repeating)
- PV1 - Patient Visit (55 fields)
- PV2 - Patient Visit Additional (51 fields)
- MRG - Merge Patient Information
Clinical (7)
- AL1 - Allergy Information (repeating)
- DG1 - Diagnosis Information (27 fields, repeating)
- OBX - Observation/Result (26 fields, repeating)
- OBR - Observation Request (45 fields, repeating)
- NTE - Notes and Comments (repeating)
- PR1 - Procedures (18 fields, repeating)
- CTI - Clinical Trial Identification
Financial & Insurance (5)
- IN1 - Insurance Information (33 fields, repeating)
- IN2 - Insurance Additional Information (20 fields)
- GT1 - Guarantor (20 fields, repeating)
- DRG - Diagnosis Related Group (11 fields)
- FT1 - Financial Transaction (26 fields, repeating)
Pharmacy Orders (7)
- ORC - Common Order (34 fields, repeating)
- RXO - Pharmacy Prescription Order (37 fields)
- RXE - Pharmacy Encoded Order (46 fields)
- RXR - Pharmacy Route (7 fields, repeating)
- RXC - Pharmacy Component (10 fields, repeating)
- TQ1 - Timing Quantity (15 fields, repeating)
- TQ2 - Timing/Quantity Relationship (11 fields, repeating)
Pharmacy Treatment (3)
- RXA - Pharmacy/Treatment Administration (25 fields, repeating)
- RXD - Pharmacy/Treatment Dispense (24 fields, repeating)
- RXG - Pharmacy/Treatment Give (25 fields, repeating)
Laboratory (2)
- SPM - Specimen (29 fields, repeating)
- SAC - Specimen Container Detail (44 fields, repeating)
Scheduling (4)
- SCH - Scheduling Activity Information (27 fields)
- AIL - Appointment Information - Location Resource (12 fields, repeating)
- AIP - Appointment Information - Personnel Resource (12 fields, repeating)
- AIS - Appointment Information - Service (12 fields, repeating)
Dietary (2)
- ODS - Dietary Orders, Supplements, and Preferences (repeating)
- ODT - Diet Tray Instructions (repeating)
Query (5)
- QPD - Query Parameter Definition (variable fields)
- RCP - Response Control Parameter (7 fields)
- DSC - Continuation Pointer (2 fields)
- QRD - Query Definition (12 fields) - legacy support
- QRF - Query Filter (9 fields) - legacy support
Administrative (3)
- ROL - Role (12 fields, repeating)
- ACC - Accident Information (6 fields)
- CTD - Contact Data (7 fields, repeating)
Total Fields: 1,100+ strongly-typed fields across all segments
See: Complete Segment Status | Phase 2 Report
📋 Supported Message Types
| Message Type | Support | Segments Available |
|---|---|---|
| ADT^A01-A14 | ✅ Complete | MSH, EVN, PID, PD1, NK1, PV1, PV2, MRG, DG1, PR1, GT1, IN1, IN2, AL1, ACC, ROL |
| ADT^A34-A40 | ✅ Complete | Patient merge/link operations with MRG |
| RDE^O11 | ✅ Complete | Pharmacy encoded order: ORC, RXE, RXR, RXC, TQ1, TQ2, NTE, OBX |
| RAS^O17 | ✅ Complete | Pharmacy administration: ORC, RXA, NTE, OBX |
| RDS^O13 | ✅ Complete | Pharmacy dispense: ORC, RXD, NTE, OBX |
| RGV^O15 | ✅ Complete | Pharmacy give: ORC, RXG, NTE, OBX |
| BAR^P01/P02 | ✅ Complete | Billing account: MSH, EVN, PID, PV1, GT1, IN1, IN2, DRG, FT1, PR1, DG1 |
| DFT^P03 | ✅ Complete | Detailed financial transaction: MSH, EVN, PID, PV1, FT1, DG1, PR1 |
| ORU^R01 | ✅ Complete | Lab results: MSH, PID, PV1, ORC, OBR, OBX, SPM, SAC, NTE, CTI |
| SIU^S12-S26 | ✅ Complete | Scheduling: MSH, SCH, PID, PV1, AIL, AIP, AIS, NTE |
| QBP | ✅ Complete | Query: MSH, QPD, RCP, DSC |
| QRY (Legacy) | ✅ Complete | Legacy query: MSH, QRD, QRF, DSC |
| ORM^O01 (Dietary) | ✅ Complete | Diet orders: MSH, PID, PV1, ORC, ODS, ODT, NTE |
Message Coverage: 12+ message types fully supported
Workflow Coverage: Patient management, pharmacy lifecycle, laboratory, scheduling, financial, queries
⚙️ Configuration
Default Options
var result = HL7Serializer.Deserialize(message);
Medication Order Grouping
var options = SerializerOptions.ForMedicationOrders();
var result = HL7Serializer.Deserialize(message, options);
// Access grouped orders
foreach (var order in result.Value.Orders)
{
var orc = order.Orc; // Common order
var rxe = order.Rxe; // Pharmacy encoded order
var routes = order.RXR; // Routes (repeating)
var components = order.RXC; // Components (repeating)
var timing = order.TQ1; // Timing (repeating)
}
Lab Order Grouping
var options = SerializerOptions.ForLabOrders();
// Groups OBR with associated OBX observations
Custom Configuration
var options = new SerializerOptions
{
ErrorHandling = ErrorHandlingStrategy.CollectAndContinue,
IgnoreUnknownSegments = true,
InitialBufferSize = 16384,
OrderGrouping = OrderGroupingConfiguration.Medication,
SegmentRegistry = customRegistry
};
Error Handling Strategies
// FailFast: Stop on first error (default)
ErrorHandlingStrategy.FailFast
// CollectAndContinue: Parse what you can, collect errors
ErrorHandlingStrategy.CollectAndContinue
// Silent: Ignore all errors (not recommended for production)
ErrorHandlingStrategy.Silent
🎨 Custom Segments
Adding custom Z-segments is straightforward:
using KeryxPars.HL7.Contracts;
using KeryxPars.HL7.DataTypes.Primitive;
public class ZPI : ISegment // Custom patient insurance
{
public string SegmentId => nameof(ZPI);
public SegmentType SegmentType { get; } = SegmentType.Universal;
public ST PolicyNumber { get; set; }
public ST GroupNumber { get; set; }
public ST EffectiveDate { get; set; }
public void SetValue(string value, int element)
{
switch (element)
{
case 1: PolicyNumber = new ST(value); break;
case 2: GroupNumber = new ST(value); break;
case 3: EffectiveDate = new ST(value); break;
}
}
public string[] GetValues()
{
var delimiters = HL7Delimiters.Default;
return
[
SegmentId,
PolicyNumber.ToHL7String(delimiters),
GroupNumber.ToHL7String(delimiters),
EffectiveDate.ToHL7String(delimiters)
];
}
public string? GetField(int index) => index switch
{
0 => SegmentId,
1 => PolicyNumber.Value,
2 => GroupNumber.Value,
3 => EffectiveDate.Value,
_ => null
};
}
// Register with generic converter (no custom converter needed!)
var registry = new DefaultSegmentRegistry();
registry.Register(new GenericSegmentConverter<ZPI>());
var options = new SerializerOptions { SegmentRegistry = registry };
Architecture
All segments follow consistent patterns:
- Generic converter support
- Strongly-typed properties
- HL7 data type compliance
- Zero-allocation parsing
- Comprehensive XML documentation
🤝 Contributing
Contributions are welcome! See our guides:
- Adding Segments Guide
- GitHub Issues for bug reports
- GitHub Discussions for questions
Development Setup
git clone https://github.com/theelevators/KeryxPars.git
cd KeryxPars
dotnet build
dotnet test
Run Benchmarks
cd KeryxPars.Benchmarks
dotnet run -c Release
📋 Requirements
- .NET 8.0 or higher
- C# 12.0 features (collection expressions, primary constructors)
- Visual Studio 2022 / JetBrains Rider / VS Code with C# Dev Kit
📜 License
Licensed under Apache License 2.0 - see LICENSE
The Apache 2.0 license provides:
- ✅ Commercial use
- ✅ Modification
- ✅ Distribution
- ✅ Patent protection
- ✅ Private use
Perfect for healthcare IT with enterprise-friendly terms and patent protection.
🙏 Acknowledgments
- Inspired by System.Text.Json performance and API design
- Built on modern .NET:
Span<T>,ref struct, source generators - Benchmarked with BenchmarkDotNet
- Validated against real-world healthcare integration scenarios
📞 Contact
- 🐛 Issues: GitHub Issues
- 💬 Discussions: GitHub Discussions
- 📧 Email: For security or private inquiries
<div align="center">
KeryxPars - High-performance HL7 parsing for modern .NET 🏥⚡
50 segments • 12+ message types • Zero custom converters • Production ready
42% HL7 v2.5 Coverage and Growing
Made with ❤️ for the healthcare community
</div>
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | 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. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net8.0
- No dependencies.
NuGet packages (1)
Showing the top 1 NuGet packages that depend on KeryxPars.Core:
| Package | Downloads |
|---|---|
|
KeryxPars.HL7
Ultra-fast HL7 v2.x message parser and serializer with zero-allocation span-based processing. Includes source-generated validation and fluent API. 50-100x faster than traditional parsers. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.5.2-beta | 106 | 1/21/2026 |
| 0.3.3-beta | 92 | 1/20/2026 |
| 0.3.2-beta | 82 | 1/20/2026 |
| 0.3.1-beta | 85 | 1/20/2026 |
| 0.3.0-beta3 | 84 | 1/20/2026 |
| 0.2.0-beta2 | 86 | 1/20/2026 |
| 0.1.0-beta1 | 83 | 1/20/2026 |