Skip to content

Latest commit

 

History

79 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

CI Made by singl3focus PRs welcome

HL7 Converter

hl7-converter is a config-driven Go toolkit for transforming HL7, ASTM, and other row-based lab messages with declarative JSON mappings.

It is built for teams that need a small, embeddable mapping engine inside LIS bridges, ETL adapters, middleware services, or device integrations, without committing to a heavyweight integration platform.

What Problem It Solves

Lab and device integrations usually fail in the same place: the message structure is close to HL7, but never close enough.

One analyzer emits ASTM-like rows, another vendor expects HL7-ish segments, and the integration layer ends up full of one-off string parsing code. That code is hard to review, hard to test, and expensive to change when a new device or customer mapping appears.

hl7-converter moves that logic into JSON configuration:

  • define separators and row structure once
  • map input tags to output tags
  • describe field templates with links like <TAG-3> or <TAG-3.1>
  • add aliases for downstream routing or logging
  • optionally post-process the converted result with trusted JavaScript

When To Use It

  • You need a small Go library, not a full integration server.
  • Message transformation rules should live in config instead of hand-written code.
  • The source and target formats are row-oriented and field-position driven.
  • You need to embed conversion into an existing Go service or adapter.
  • You want repeatable test coverage around mappings and templates.

When Not To Use It

  • You need a visual integration designer, queues, retries, persistence, or orchestration.
  • Your mappings require deep business workflows rather than deterministic field transformation.
  • Your payloads are not fundamentally row/tag based.
  • You need sandboxed user scripting. JavaScript hooks here are trusted-code only.

30-Second Quick Start

go get github.com/singl3focus/hl7-converter/v2@latest
package main

import (
	"log"

	hl7converter "github.com/singl3focus/hl7-converter/v2"
)

func main() {
	params, err := hl7converter.NewConverterParams("./examples/config.json", "astm_hbl", "mindray_hbl")
	if err != nil {
		log.Fatal(err)
	}

	converter, err := hl7converter.NewConverter(
		params,
		hl7converter.WithUsingPositions(),
		hl7converter.WithUsingAliases(),
	)
	if err != nil {
		log.Fatal(err)
	}

	input := []byte("H|\\^&|||sireAstmCom|||||||P|LIS02-A2|20220327\n" +
		"P|1||||^||||||||||||||||||||||||||||\n" +
		"O|1|142212||^^^Urina4^screening^|||||||||^||URI^^||||||||||F|||||\n" +
		"R|1|^^^Urina4^screening^^tempo-analisi-minuti|180|||||F|||||\n" +
		"R|2|^^^Urina4^screening^^tempo-analisi-minuti|90|||||F|||||")

	result, err := converter.Convert(input)
	if err != nil {
		log.Fatal(err)
	}

	log.Print(result.String())
}

Repository sample config: examples/config.json.

Input / Output Example

Input:

H|\^&|||sireAstmCom|||||||P|LIS02-A2|20220327
P|1||||^||||||||||||||||||||||||||||
O|1|142212||^^^Urina4^screening^|||||||||^||URI^^||||||||||F|||||
R|1|^^^Urina4^screening^^tempo-analisi-minuti|180|||||F|||||
R|2|^^^Urina4^screening^^tempo-analisi-minuti|90|||||F|||||

Output:

MSH|^\&|Manufacturer|Model|||20220327||ORU^R01||P|2.3.1||||||ASCII|
PID||142212|||||||||||||||||||||||||||
OBR||142212|||||||||||||URI|||||||||||||||||||||||||||
OBX|||Urina4^screening^tempo-analisi-minuti|tempo-analisi-minuti|180||||||F|||||
OBX|||Urina4^screening^tempo-analisi-minuti|tempo-analisi-minuti|90||||||F|||||

Mapping Config Example

{
  "mindray_hbl": {
    "component_separator": "^",
    "component_array_separator": " ",
    "field_separator": "|",
    "line_separator": "\r",
    "aliases": {
      "Header": "MSH-9.2",
      "PatientID": "PID-3",
      "Key": "OBR-16"
    },
    "tags_info": {
      "positions": {
        "1": "MSH",
        "2": "PID",
        "3": "OBR",
        "4": "OBX"
      },
      "tags": {
        "MSH": {
          "linked": "H",
          "options": ["autofill"],
          "fields_number": 19,
          "template": "MSH|??^\\&|??Manufacturer|??Model|||<H-14>||??ORU^R01|<H-3>|<H-12>|??2.3.1||||||??ASCII|"
        },
        "PID": {
          "linked": "P",
          "fields_number": 30,
          "template": "PID||<O-3>|||||||||||||||||||||||||||"
        }
      }
    }
  }
}

Architecture Overview

Conversion Flow

flowchart LR
    A["Incoming message"] --> B["Parse rows by line separator"]
    B --> C["Split fields by field separator"]
    C --> D["Apply input tag options"]
    D --> E["Build parsed tag map"]
    E --> F["Resolve output templates and links"]
    F --> G["Generate rows by input order or configured positions"]
    G --> H["Apply aliases"]
    H --> I["Optional JS post-processing"]
    I --> J["Result"]
Loading

Config Structure

flowchart TB
    A["Config file"] --> B["Modification block"]
    B --> C["Separators"]
    B --> D["Types"]
    B --> E["Aliases"]
    B --> F["tags_info"]
    F --> G["positions"]
    F --> H["tags"]
    H --> I["linked"]
    H --> J["fields_number"]
    H --> K["template"]
    H --> L["options"]
Loading

Conversion Modes

flowchart LR
    A["Input-driven conversion"] --> B["Walk input rows in original order"]
    B --> C["Map each seen input tag to one output row"]

    D["Position-driven conversion"] --> E["Walk output positions from config"]
    E --> F["Repeat each output tag by linked input count"]
Loading

Supported Mapping Features

  • Custom line, field, component, and component-array separators.
  • Output generation by input order or by explicit configured positions.
  • Template links like <TAG-3> and component links like <TAG-3.1>.
  • Default literals with ??value.
  • Aliases for extracting stable values from the converted result.
  • Trusted JavaScript post-processing via Result.UseScript.

Supported Options

  • autofill: append empty trailing fields while parsing an input row until it matches the tag fields_number.

Validation Model

Validation now happens in two layers:

  1. Modification.Validate()
    • checks required separators
    • rejects separator conflicts
    • validates positions keys and references
    • validates known options
    • validates template field count and link/default syntax
  2. NewConverterParams()
    • validates the selected input/output pair together
    • rejects output templates that reference unknown input tags
    • rejects links that point outside known input field ranges when fields_number is explicit

This means invalid mappings fail earlier, before conversion starts.

Concurrency And Safety

  • Converter is safe for concurrent Convert calls on the same instance.
  • Result is mutable and is not guaranteed safe for concurrent mutation.
  • JavaScript hooks are trusted-code only. There is no built-in sandbox or timeout.

Public API Snapshot

  • Construction: NewConverterParams, NewConverter
  • Converter options: WithUsingPositions, WithUsingAliases
  • Execution: Convert, ParseMsg, ParseInput, IndetifyMsg
  • Result helpers: FindTag, ApplyAliases, UseScript, SwapRows, SetRow
  • Field helpers: Components, Array, ComponentsChecked, ArrayChecked

Limitations

  • Designed for ASCII-oriented messages and simple string-based separators.
  • Template semantics are intentionally narrow: deterministic field mapping, not workflow orchestration.
  • Component validation can only be checked at runtime because input payload shape may vary per message.
  • JavaScript support is for controlled operational environments, not end-user scripting.

CLI

A minimal CLI is published with each release as hl7conv. It is built from cmd/hl7conv and uses the same library API.

hl7conv <command> [flags]

Commands:
  convert         Convert a message using a config and an input/output block pair.
  identify        Identify a message type by matching parsed tags against configured Types.
  validate-config Validate a JSON config file and optionally a single modification block.
  version         Print version.

Examples:

# convert a message read from a file, position-driven, with aliases
hl7conv convert \
  -config ./examples/config.json \
  -in astm_hbl -out mindray_hbl \
  -input ./message.txt \
  -positions -aliases

# validate config and a specific block
hl7conv validate-config -config ./examples/config.json -block astm_hbl

# identify message type
cat ./message.txt | hl7conv identify \
  -config ./examples/config.json \
  -in astm_hbl -out mindray_hbl

Build locally:

go build -o hl7conv ./cmd/hl7conv

Alternatives

hl7-converter deliberately occupies a small slot. It is not a replacement for full integration platforms or full HL7 toolkits.

  • Mirth Connect / NextGen Connect. Full integration server with channels, queues, persistence, retries, transformers and a UI. Use it when you need an integration platform. Use hl7-converter when you need an embeddable mapping engine inside your own Go service.
  • HAPI HL7v2 (Java). Comprehensive HL7v2 parser and model with strict structures. Use it when you need full HL7v2 conformance, ACK handling, and a Java stack. Use hl7-converter when your reality is closer to ASTM-like or HL7-ish row formats and you want config-driven mapping in Go.
  • FHIR-oriented converters and mappers (FHIR Mapping Language, Microsoft FHIR Converter). Aimed at producing FHIR resources, often with rich resource semantics and bundle handling. Use them when the target is FHIR. Use hl7-converter when both sides are row-and-tag based and you need deterministic field mapping without leaving Go.

In short: pick hl7-converter when the mapping is deterministic, row-based, must live in Go, and the only thing you really need is a small, testable transformation step.

Testing And Benchmarks

Run the full test suite:

go test ./...

Run with the race detector:

go test -race ./...

Run benchmarks:

go test -bench=. ./...

The repository includes:

  • unit tests for config validation, template parsing, conversion modes, aliases, and field helpers
  • concurrency tests for shared Converter usage
  • fuzz coverage for Convert
  • benchmarks for realistic message payloads

License

MIT

About

Go toolkit for converting HL7/ASTM-style lab messages via declarative JSON mappings. Validated configs, positional or input-driven generation, optional JS post-processing, and aliases for easy routing. Ideal for LIS connectors, ETL bridges, and quick vendor-to-vendor integrations

Topics

Resources

Contributing

Stars

14 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages