edifact.lua
Verified
Added by iNTERFACEWARE
Convert EDI messages to HL7 format so you can process them like an HL7 message (and convert them back to EDI afterwards)
Source Code
------------------------------------------------------------------
-- --
-- Utilities for EDIFACT Messages --
-- --
-- Copyright (c) 2012 iNTERFACEWARE Inc. All Rights Reserved. --
-- --
------------------------------------------------------------------
local edifact = {}
-- Define the header patterns in one place.
local DEL = '([^%w ])' -- A delimiter.
local UNA = '^UNA'..DEL:rep(3)..'(%W)(%W)'..DEL
local MSH = '^MSH'..DEL:rep(5)..'%1' -- HL7
-- Some functions take a EDIFACT header as an
-- argument; this is used when one is not given.
local DefaultHeader = "UNA:+.?*'"
-- Convert EDIFACT to HL7 with standard EDIFACT
-- delimiters, HL7 escapes but no MSH header.
--
function edifact.toHl7(Data)
-- These are the delimiters we want. These will
-- replace the input delimiters in the same order.
local Output = { '^', '|', '.', '', '~', '\r' }
local Escape = {'\\S\\', '\\F\\', '.', '\\E\\', '\\R\\', '\\X0D\\'}
local Header = 'MSH|^~\\&|||||||EDIFACT\r'
-- These are the delimiters in the message.
local Input = { select(3, Data:find(UNA)) }
if #Input == 0 then -- Panic!
error('No UNA segment found.', 2)
end
-- We start with a simple map from the input
-- delimiters to what we want for output.
local BasicMap = {}
for I,Get in ipairs(Input) do
local Want = Output[I]
Escape[Want] = Escape[I]
BasicMap[Get] = Want
end
-- We expand this map to include checks for
-- output delimiters in the input, and create
-- a pattern to match all special characters.
local Map, Pattern = { ['&']='\\T\\' }, '[%&'
for Get,Want in pairs(BasicMap) do
Map[Get] = Want
Pattern = Pattern..'%'..Get
-- Any output delimiters found in the
-- input must be escaped in the output.
if not BasicMap[Want] then
Map[Want] = Escape[Want]
Pattern = Pattern..'%'..Want
end
end
-- If the input contains a release character,
-- we expand the map and pattern further to
-- handle escaped delimiters in the input.
local Release = Input[4]
if Release ~= ' ' then
-- If a release character occurs before
-- a non-delimiter, we leave it as-is.
Map[Release] = Escape[Release] or Release
-- Otherwise, we replace the pair with the
-- second delimiter, escaped if necessary.
Pattern = '%'..Release..'?'..Pattern
BasicMap, Map = Map, {}
for Get,Want in pairs(BasicMap) do
Map[Get] = Want
Map[Release..Get] = Escape[Get] or Get
end
end
Map[' '] = nil
Pattern = Pattern:gsub('%% ','') .. ']'
local Out = Data:sub(10):gsub(Pattern, Map)
return Header..Out
end
-- Convert HL7 formatted EDIFACT to real EDIFACT
-- with the removal of HL7 escapes and replace
-- the MSH header with an EDIFACT UNA header.
--
function edifact.fromHl7(Data, NewHeader)
local Dec, Seg = '.', '\r'
local Ok,_,Fld,Com,Rep,Esc,Sub = Data:find(MSH)
assert(Ok, 'Invalid/missing MSH segment.')
Data = Data:gsub('MSH.-\r','')
local Header = table.concat{
'UNA',Com,Fld,Dec,Esc,Rep,Seg}
local Escape = {
['\\'] = '\\', -- Special in HL7 only.
[Com] = Esc..Com, [Fld] = Esc..Fld,
[Esc] = Esc..Esc, [Seg] = Esc..Seg,
[Rep] = (Rep ~= ' ') and Esc..Rep
}
-- First we escape every occurance of the
-- release character in the input.
local Out = Data
if #Esc > 0 then -- Assuming we need to.
Data:gsub('%'..Esc, Escape[Esc])
end
-- Then we replace all HL7 escapes with
-- EDIFACT escapes or regular characters.
Out = Out:gsub('(\\%u%x?%x?\\)',
setmetatable({
['\\S\\'] = Escape[Com],
['\\F\\'] = Escape[Fld],
['\\E\\'] = Escape['\\'],
['\\R\\'] = Escape[Rep],
['\\T\\'] = Sub, -- Not special.
-- ['\\X27\\'] = Escape[Seg],
}, {
-- This handles other \X..\ escapes
-- which could be present in HL7.
__index = function(self, s)
local Out = s:gsub('\\X(%x%x)\\',
function(hex)
local i = tonumber(hex,16)
return string.char(i)
end)
Out = Escape[Out] or Out
self[s] = Out -- Memoize.
return Out
end
}))
Out = edifact.clean(Header..Out, NewHeader)
return Out
end
-- Replace the delimiters of an EDIFACT message
-- with those in the header provided or use the
-- default EDIFACT delimiters.
--
function edifact.clean(Data, Header)
Header = Header or DefaultHeader
local Input = { select(3, Data :find(UNA)) }
local Output = { select(3, Header:find(UNA..'$')) }
local InpRelease, OutRelease = Input[4], Output[4]
if not InpRelease then error('Bad UNA header in message.', 2) end
if not OutRelease then error('Invalid output header.', 2) end
local Escape, Map, Pattern = {}, {}, '['
if OutRelease ~= ' ' then
for _,Want in ipairs(Output) do
Escape[Want] = OutRelease..Want
Map[Want] = OutRelease..Want
Pattern = Pattern..'%'..Want
end
end
for I,Get in ipairs(Input) do
local Want = Output[I]
Map[Get] = Want
Pattern = Pattern..'%'..Get
end
if InpRelease ~= ' ' then
Pattern = '%'..InpRelease..'?'..Pattern
for I,Get in ipairs(Input) do
Map[InpRelease..Get] = Escape[Get] or Get
end
end
Map[' '] = nil
Pattern = Pattern:gsub('%% ','') .. ']'
local Out = Data:sub(10):gsub(Pattern,Map)
return Header..Out
end
return edifact
Description
Convert EDI messages to HL7 format so you can process them like an HL7 message (and convert them back to EDI afterwards)
Attachments
Usage Details
EDIFACT and HL7 are very similar. The main difference is how delimiters are escaped when they occur in a message. For instance, in the following EDIFACT snippet, the question-mark (?) is used to escape delimiters, or “release” them in EDIFACT parlance.
This module allows you to convert messages back an forth between EDI and HL7. This means you can convert an incoming EDI message to HL7, then process it as an HL7 message (hl7.parse{}, map fields etc), then convert it back to EDI and forward it.
How to use edifact.lua:
- Use a Filter or To Translator component
- Create the edifact module and paste in the code
Note: You can also load the attached project edifact.zip - Add some sample HL7 messages from SampleData.txt
Note: The project already contains the sample data - Load sample EDI messages if you have some (useful but not required)
- Convert HL7 to EDI with
edifact.fromHl7(), then convert it back with edifact.toHl7() - Play with the code and compare annotations to see how it works
Here is some sample code for main():
local edifact = require 'edifact' function main(Data) local edi = edifact.fromHl7(Data) -- convert to EDI local hl7 = edifact.toHl7(edi) -- convert back to HL7 trace(edi) trace(hl7) end
More Information