hl7v2_parser

Pub Version Build Status License: BSD-3-Clause

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.