This post was originally written for Iguana 5 so it contains version 5 screenshots, and may contain out of date references.
Have you ever needed to generate an HL7 message with non-standard delimiters? It happens; the receiving party is expecting their own flavor of HL7 with strange delimiters – what do you do? You can use this function, hl7.serialize{}, which will serialize an HL7 message with a provided set of delimiters (and/or a provided set of “escape characters”).
Note: to encode using the default HL7 delimiters just omit the delimiters parameter – this is demonstrated by the first hl7.serialize{} call in the screenshot.
Here’s how it looks in use:

Pay attention to MSH.8, which contains every type of escape sequence. And here’s the code for the function, created with some help from Eric Mulvaney. I put this into its own module, “hl7utils.serialize”, but “hl7.serialize” or “serialize” would make just as much sense.
-- Will search for instances of Pattern in S.
-- Will call replacement function onUnmatched
-- on unmatched parts of S, and onMatched on
-- matched parts of S.
--
local function search(S, Pattern, onUnmatched, onMatched)
Pattern = '^(.-)('..Pattern..')(.*)$'
local Out = {}
local function loop(S)
local Head, Match, Tail = select(3, S:find(Pattern))
if Match then
table.insert(Out, (onUnmatched(Head)) )
table.insert(Out, (onMatched(Match)) )
return loop(Tail)
else
table.insert(Out, (onUnmatched(S)) )
return table.concat(Out)
end
end
return loop(S)
end
local charsToEscape = {['(']=1, [')']=1, ['.']=1, ['%']=1, ['+']=1, ['-']=1,
['*']=1, ['?']=1, ['[']=1, ['^']=1, ['$']=1}
local function charToPattern(C)
if charsToEscape[C] then
return '%'..C
else
return C
end
end
-- Only for HL7 Messages
-- Accepts a table which must contain the following parameter:
-- data: the HL7 message to be serialized.
-- Also accepts the following optional parameters:
-- delimiters: a list of delimiters to use in the message,
-- if different from the default {'\r', '|', '^', '~', '\\', '&'}.
-- escaped: a list representing the character used to represent
-- an escaped delimiter character, if different from the default
-- {'F', 'S', 'R', 'E', 'T'}.
--
function hl7.serialize(Params)
Params = Params or {}
local Msg = Params.data
local NodeType, ProtocolType = Msg:nodeType()
if NodeType ~= 'message' or ProtocolType ~= 'hl7' then
error('Expected hl7 message, got '..ProtocolType..' '..NodeType, 2)
end
local Serialized = tostring(Msg)
local Delimiters = Params.delimiters or {'\r', '|', '^', '~', '\\', '&'}
if Delimiters and #Delimiters ~= 6 then
error('Expected delimiter list of size 6, got '..#Delimiters, 2)
end
for N,D in ipairs(Delimiters) do
if #D ~= 1 then
error('Delimiters must be exactly one character, "'..D..'" is '..#D, 2)
end
end
local Escaped = Params.escaped or {'F', 'S', 'R', 'E', 'T'}
if #Escaped ~= 5 then
error('Expected escaped list of size 5, got '..#Escaped, 2)
end
local DelimiterMap = {
['\r']=Delimiters[1],
['|']=Delimiters[2],
['^']=Delimiters[3],
['~']=Delimiters[4],
['\\']=Delimiters[5],
['&']=Delimiters[6]
}
local UnescapeMap = {
['\\F\\']='|',
['\\S\\']='^',
['\\R\\']='~',
['\\E\\']='\\',
['\\T\\']='&'
}
local EscapeNewDelimiterMap = {
[Delimiters[1]]=Delimiters[5]..'X'..Delimiters[1]:byte(1)..Delimiters[5],
[Delimiters[2]]=Delimiters[5]..Escaped[1]..Delimiters[5],
[Delimiters[3]]=Delimiters[5]..Escaped[2]..Delimiters[5],
[Delimiters[4]]=Delimiters[5]..Escaped[3]..Delimiters[5],
[Delimiters[5]]=Delimiters[5]..Escaped[4]..Delimiters[5],
[Delimiters[6]]=Delimiters[5]..Escaped[5]..Delimiters[5]
}
local DelimiterPattern = '[\r|%^~\\&'..
charToPattern(Delimiters[1])..
charToPattern(Delimiters[2])..
charToPattern(Delimiters[3])..
charToPattern(Delimiters[4])..
charToPattern(Delimiters[5])..
charToPattern(Delimiters[6])..']'
Serialized = search(Serialized, '\\[FSRET]\\',
function(Unmatched)
return Unmatched:gsub(DelimiterPattern,
function(Match)
return DelimiterMap[Match] or EscapeNewDelimiterMap[Match]
end
)
end,
function(Matched)
local Unescaped = UnescapeMap[Matched]
local NewEscape = EscapeNewDelimiterMap[Unescaped]
if NewEscape then
return NewEscape
else
return Unescaped
end
end)
return Serialized
end