hl7v2_parser
Parse, generate, and access HL7v2 messages in pure Dart.
The first comprehensive HL7v2 library for Dart and Flutter. Zero dependencies, all platforms (web, mobile, server). Lazy parser that handles 490K+ messages per second.
No more Python/Java sidecars. If your Flutter app talks to hospital systems (LIS, RIS, analyzers, EMRs), this package gives you native HL7v2 support.
Quick Start
# pubspec.yaml
dependencies:
hl7v2_parser: ^0.1.0
import 'package:hl7v2_parser/hl7v2_parser.dart';
final raw = 'MSH|^~\\&|LAB|HOSP|EMR|HOSP|20240101120000||ORU^R01|MSG001|P|2.5\r'
'PID|||12345^^^HOSP^MR||DOE^JOHN^A||19800101|M\r'
'OBX|1|NM|WBC^White Blood Cell||7.2|10*3/uL|4.5-11.0|N|||F\r';
final message = Message.parse(raw);
// Index access
print(message.segment('PID')!.field(5).first.value); // DOE^JOHN^A
// Terser path access
print(message.get('PID-5-2')); // JOHN
// Typed segment access
print(message.pid!.patientName); // DOE^JOHN^A (XpnValue)
print(message.pid!.patientName!.givenName); // JOHN
Features
Parse any HL7v2 message
Lazy parser — segments are split upfront, fields/components parsed on first access. Custom delimiters and encoding characters are fully supported.
final message = Message.parse(raw);
print(message.messageType); // ORU
print(message.triggerEvent); // R01
print(message.version); // 2.5
print(message.segments.length); // 3
Three ways to access data
1. Index-based — direct positional access, fastest for known field numbers:
final pid = message.segment('PID')!;
final name = pid.field(5).first.component(1).value; // Family name
2. Terser path — string-based path like PID-5-2, familiar to HL7 developers:
final givenName = message.get('PID-5-2'); // JOHN
message.set('PID-5-2', 'JANE'); // Modify in place
3. Typed segment extensions — named getters with composite data types:
final pid = message.pid!;
print(pid.patientName!.familyName); // DOE
print(pid.patientName!.givenName); // JOHN
print(pid.dateOfBirth); // 19800101
print(pid.administrativeSex); // M
Generate HL7v2 wire format
Round-trip fidelity: Message.parse(raw).encode() == normalize(raw) for all
tested messages. Trailing empty fields are stripped at every level.
final encoded = message.encode();
// MSH|^~\&|LAB|HOSP|EMR|HOSP|20240101120000||ORU^R01|MSG001|P|2.5\r...
Build messages from scratch
Fluent builder API with automatic MSH-7 (timestamp) and MSH-10 (control ID) generation.
final msg = MessageBuilder()
.msh((b) => b
.sendingApplication('LAB')
.sendingFacility('HOSP')
.receivingApplication('EMR')
.receivingFacility('HOSP')
.messageType('ORU', 'R01')
.version('2.5'))
.addSegment('PID', (b) => b
.field(3, '12345^^^HOSP^MR')
.field(5, 'DOE^JOHN^A')
.field(7, '19800101')
.field(8, 'M'))
.addSegment('OBX', (b) => b
.field(1, '1')
.field(2, 'NM')
.field(3, 'WBC^White Blood Cell')
.field(5, '7.2')
.field(6, '10*3/uL')
.field(7, '4.5-11.0')
.field(8, 'N')
.field(11, 'F'))
.build();
ACK/NAK generation
Generate acknowledgement messages per HL7v2 spec. Supports both original mode (AA/AE/AR) and enhanced mode (CA/CE/CR).
final ack = message.generateAck(code: AckCode.aa);
print(ack.get('MSA-1')); // AA
print(ack.get('MSA-2')); // MSG001 (echoes original control ID)
// Rejection with error message
final nak = message.generateAck(
code: AckCode.ar,
errorMessage: 'Unknown patient ID',
);
Structural validation
Validate messages against segment definitions — required fields, data types, max lengths, unknown segments.
final errors = message.validate();
for (final error in errors) {
print('${error.severity}: ${error.message} '
'(${error.segment}:${error.fieldNumber})');
}
Escape handling
Full HL7v2 escape sequence support including \F\, \S\, \R\, \E\, \T\,
hex (\Xhh\), and custom-delimiter escapes. Automatic unescape on read,
escape on write.
// Raw value preserves escapes, .value unescapes
final raw = pid.field(5).first.rawValue; // Smith\T\Jones
final val = pid.field(5).first.value; // Smith&Jones
25 typed segment extensions
Named getters for commonly used segments. Composite data types (XPN, CWE, CX, XAD, etc.) are returned as typed objects with named component access.
| Segment | Description |
|---|---|
| MSH | Message Header |
| PID | Patient Identification |
| PV1 | Patient Visit |
| OBR | Observation Request |
| OBX | Observation/Result |
| ORC | Common Order |
| MSA | Message Acknowledgement |
| EVN | Event Type |
| ERR | Error |
| NK1 | Next of Kin |
| AL1 | Allergy Information |
| DG1 | Diagnosis |
| FT1 | Financial Transaction |
| GT1 | Guarantor |
| IAM | Patient Adverse Reaction |
| IN1 | Insurance |
| MRG | Merge Patient Information |
| NTE | Notes and Comments |
| PD1 | Patient Additional Demographics |
| ROL | Role |
| RXA | Pharmacy/Treatment Administration |
| RXO | Pharmacy/Treatment Order |
| SCH | Scheduling Activity Information |
| SPM | Specimen |
| TXA | Transcription Document Header |
Z-segments parse transparently — access their fields by index or Terser path.
16 composite data types
XpnValue, CweValue, CxValue, XadValue, XcnValue, XtnValue,
HdValue, EiValue, PlValue, PtValue, VidValue, MsgValue,
CqValue, ErlValue, FcValue, SnValue — all with named component getters
and automatic unescaping.
Benchmarks
Measured on Apple M-series, Dart 3.8.1 (stable), macOS. Each benchmark runs for ≥3 seconds after 100 warmup iterations.
| Benchmark | msg/s | avg latency |
|---|---|---|
| Parse ADT^A01 (6 segments) | 493,908 | 2.0 µs |
| Parse ORM^O01 (7 segments) | 508,266 | 2.0 µs |
| Parse ORU^R01 (11 segments) | 442,369 | 2.3 µs |
| Parse Large ORU (55 segments) | 80,878 | 12.4 µs |
| Round-trip ADT^A01 | 34,925 | 28.6 µs |
| Round-trip ORU^R01 | 30,859 | 32.4 µs |
| Terser 10-path lookup | 165,333 | 6.0 µs |
Platform Support
| Platform | hl7v2 | hl7v2_mllp |
|---|---|---|
| Dart VM (server) | Yes | Yes |
| Flutter (Android/iOS) | Yes | Yes |
| Flutter Web | Yes | No* |
| Dart CLI | Yes | Yes |
* hl7v2_mllp requires dart:io for TCP sockets. Core parsing works
everywhere.
MLLP Transport
For TCP transport over MLLP (client, server, TLS, reconnection), see the
companion package hl7v2_mllp.
Comparison
| Feature | hl7v2 | hl7_v2 v0.0.1 |
|---|---|---|
| Parse ER7 | Yes | Yes |
| Generate ER7 | Yes | No |
| Terser paths | Yes | No |
| Typed segments | 25 | 0 |
| Composite types | 16 | 0 |
| ACK/NAK generation | Yes | No |
| Message builder | Yes | No |
| Validation | Yes | No |
| Escape sequences | Full | Partial |
| Custom delimiters | Yes | No |
| MLLP transport | Separate pkg | No |
| Zero dependencies | Yes | No |
| Web platform | Yes | No |
| Benchmark (msg/s) | 490K+ | — |
Contributing
Contributions welcome! Areas of interest:
- Additional typed segment extensions (120+ segments in the HL7v2 spec)
- Version-specific schema validation
- Additional test fixtures from real hospital systems
Please file issues on GitHub.
License
BSD-3-Clause. See LICENSE.
Libraries
- hl7v2_parser
- HL7v2 parser, generator, and typed access for Dart.