- Created by Jacob Pagnotta on Jan 16, 2023
You are viewing an old version of this page. View the current version.
Compare with Current View Page History
Version 1 Next »
Summary
The default xml node and xml module provide limited functionalities to either parse or create XML node. In additional to https://help.interfaceware.com/v6/xml-channel. The XMLUtils.lua module extends further to the following capabilities:
Node.select()
Node.number()
Node.text()
Node.children()
Node.firstChild()
Node.removeText()
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
Node.select()
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 = 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 = recordXML:select("chargeTransaction/patient", true)
Node.text()
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 = recordXML:select("chargeTransaction/patient", true)
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
local patientXML = recordXML:select("chargeTransaction/patient", true)
Node.firstChild()
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 = recordXML:select("chargeTransaction/patient", true)
Node.removeText()
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 = recordXML:select("chargeTransaction/patient", true)
- No labels