Using XMLUtils.lua Module for Extended XML Functionality

The default xml node and xml module provide limited functionalities to either parse or create XML node. In additional to XML Techniques - iNTERFACEWARE Help Center. The XMLUtils.lua module extends further to the following capabilities:

XMLUtils.lua

function node.select(X, path, required) local elements = path:split('/') if not elements or #elements == 0 then return end local thisNode = X for i, v in ipairs(elements) do local nodeType = xml.ELEMENT local _, _, nodeName, predicate = v:find("^([^@]%w+)%[(.+)%]$") if not nodeName then nodeName = v if nodeName:sub(1, 1) == '@' then nodeType = xml.ATTRIBUTE nodeName = nodeName:sub(2) end end local nextNode local count = thisNode:childCount(nodeName) if count > 0 then for j = 1, count do local child = thisNode:child(nodeName, j) if child:nodeType() == nodeType then if predicate then -- predicate = child index local index = tonumber(predicate) if index then if index == j then nextNode = child break end end -- predicate = child expression local parts = predicate:split("=") if parts then local nodeValue = child:text(parts[1]) local compare = parts[2]:gsub("^['](%w+)[']$", "%1") if nodeValue == compare then nextNode = child break end else error('invalid predicate: ' .. predicate, 2) end else nextNode = child break end end end end trace(nextNode) -- set thisNode for next loop iteration thisNode = nextNode if not thisNode then if required then error('required path not found: ' .. v, 2) end return end end return thisNode end function node.number(X, path, required, default) if path then X = X:select(path, required) end local nodeValue if X then if X:nodeType() == xml.ATTRIBUTE then nodeValue = X:nodeValue() else for i = 1, #X do if X[i]:nodeType() == xml.TEXT then nodeValue = X[i]:nodeValue() break end end end end nodeValue = tonumber(nodeValue) if nodeValue then return nodeValue elseif default then return default else return 0 end end function node.text(X, path, required, default) if path then X = X:select(path, required) end local nodeValue if X then if X:nodeType() == xml.ATTRIBUTE then nodeValue = X:nodeValue() else for i = 1, #X do if X[i]:nodeType() == xml.TEXT then nodeValue = X[i]:nodeValue() break end end end end if nodeValue then return nodeValue elseif default then return default else return '' end end function node.children(X, name, matchPath, matchValue) local list = {} for i = 1, #X do if X[i]:nodeType() == xml.ELEMENT then if X[i]:nodeName() == name then if matchPath then if X[i]:text(matchPath) == matchValue then table.insert(list, X[i]) end else table.insert(list, X[i]) end end end end return list end function node.firstChild(X, name, matchPath, matchValue) for i = 1, #X do if X[i]:nodeType() == xml.ELEMENT then if X[i]:nodeName() == name then if matchPath then if X[i]:text(matchPath) == matchValue then return X[i] end else return X[i] end end end end end function node.appendText(X, name, value) local element = X:append(xml.ELEMENT, name) element:append(xml.TEXT, value) return element end function node.removeText(X) for i = #X, 1, -1 do if X[i]:nodeType() == xml.TEXT then X[i] = nil end end end -- validate required parameters (List) are present in table (ignore arg1..argN parameters) function checkParam(T, List) if type(T) ~= 'table' then error('invalid arguments, expected table', 3) end for k,v in pairs(List) do for w,x in pairs(T) do if w:find('arg') then if w == 'arg' then error('Unknown parameter "'..w..'"', 3) end else if not List[w] then error('Unknown parameter "'..w..'"', 3) end end end end end -- convert arg1..argN parameters into table array function getArgs(P) local args = {} for k,v in pairs(P) do if k:find('arg')==1 then args[tonumber(k:sub(4))] = P[k] end end return args end -- safely retry function function retry(P) checkParam(P, {func=0, retry=0, pause=0, funcname=0, errorfunc=0}) if type(P.func) ~= 'function' then error('invalid func specified, expected pointer to function', 2) end local RetryCount = P.retry or -1 local Delay = P.pause or 10 local Fname = P.funcname local Func = P.func local ErrorFunc = P.errorfunc local Success, ErrMsgOrReturnCode local Args = getArgs(P) local i = 0 if iguana.isTest() then RetryCount = 2 end while true do local R = {pcall(Func, unpack(Args))} Success = R[1] ErrMsgOrReturnCode = R[2] if ErrorFunc then Success = ErrorFunc(unpack(R)) end if Success then if i > 0 then status('green', 'Operation succeeded on retry after ' .. i .. ' attempts.') else status('green') end return unpack(R,2) else local msg = 'Error executing operation ' if Fname then msg = msg .. Fname .. '() ' end if i == 0 then msg = msg .. 'on first attempt.' else msg = msg .. 'on retry attempt ' .. i .. '.' end if RetryCount == -1 or i < RetryCount then msg = msg .. '\nSleeping for ' .. Delay .. ' seconds before next retry.' msg = msg .. '\nError: ' .. ErrMsgOrReturnCode status('yellow', msg) util.sleep(Delay * 1000) i = i + 1 else msg = msg .. '\nMax retries reached, stopping retries.' status('yellow', msg) return false, msg end end end end function iguana.getChannelStatus(channelName) if not channelName then channelName = iguana.channelName() end local iguanaStatus = iguana.status() local root = xml.parse(iguanaStatus) for i = 1, root.IguanaStatus:childCount('Channel') do local channel = root.IguanaStatus:child('Channel', i) if channel.Name:nodeValue() == channelName then return channel end end end function iguana.throttleChannel(queueSize, channelName) local channelStatus = iguana.getChannelStatus(channelName) trace(channelStatus) if not channelStatus then return true, 'unable to determine destination channel status' end local destChannelStatus = channelStatus.Status:nodeValue() if not iguana.isTest() and destChannelStatus == 'off' then return true, 'destination channel is currently off' end local destQueueSize = tonumber(channelStatus.MessagesQueued:nodeValue()) if not iguana.isTest() and destQueueSize > queueSize then return true, 'destination queue size exceeds ' .. queueSize .. ' (currently ' .. destQueueSize .. ')' end -- do not throttle channel return false end function status(color, text) if text then text = text .. '\n' .. os.date() end iguana.setChannelStatus{color = color, text = text} end function warning(text) iguana.logWarning(text) text = text .. '\n' .. os.date() iguana.setChannelStatus{color = 'yellow', text = text} end

 

How to use XMLUtil.lua

Import the above XMLUtils.lua into your translator, parse an xml, and call the following extended xml functions below:

Node.select()

USAGE

Select child node from the root XML node based on the XML path. Unlike traditional xml node drill down with nil validation, you can easily use select function to find the sub child of xml node.

REQUIRED PARAMETERS

  • path: xml child node path

  • required: If 'required' is true, the path must be exist otherwise throw “required path not found“ error.

RETURNS

  • The child node tree

Sample Code

local patientXML = recordXML:select("chargeTransaction/patient", true)

Node.number()

USAGE

Select child node from the root XML node based on the XML path.

REQUIRED PARAMETERS

  • XMLNode: the root of XML node

  • path: xml child node path

  • required: If 'required' is false and 'thisNode' is nil, the function returns nothing and exits.

RETURNS

  • The child node tree

SAMPLE CODE

local patientXML = chargeTransaction:number("batchId") .. '-' .. chargeTransaction:number("syncId")

Node.text()

USAGE

Keep the XML element but empty the XML element Text value

REQUIRED PARAMETERS

  • path: xml child node path

  • required: If 'required' is false and 'thisNode' is nil, the function returns nothing and exits.

RETURNS

  • The child node tree

SAMPLE CODE

Node.children()

USAGE

Select child node from the root XML node based on the XML path

REQUIRED PARAMETERS

  • XMLNode: the root of XML node

  • path: xml child node path

  • required: If 'required' is false and 'thisNode' is nil, the function returns nothing and exits.

RETURNS

  • The child node tree

SAMPLE CODE

Node.firstChild()

USAGE

Search for the first XML element that meets to criteria passed into the function.

REQUIRED PARAMETERS

  • Element Name: The name of the XML element.

  • Attribute Name: The attribute name that you want to be found. Make sure to include the “@” prefix.

  • Attribute Value: The attribute value attached to the attribute name to be found.

RETURNS

  • The first match for the XML element.

SAMPLE CODE

Node.removeText()

USAGE

Keep the XML element but empty the XML element Text value

REQUIRED PARAMETERS

  • None

RETURNS

  • Empty

SAMPLE CODE

Related pages