--- @module Module providing a non-validating XML stream parser in Lua. -- -- Features: -- ========= -- -- * Tokenises well-formed XML (relatively robustly) -- * Flexible handler based event API (see below) -- * Parses all XML Infoset elements - ie. -- - Tags -- - Text -- - Comments -- - CDATA -- - XML Decl -- - Processing Instructions -- - DOCTYPE declarations -- * Provides limited well-formedness checking -- (checks for basic syntax & balanced tags only) -- * Flexible whitespace handling (selectable) -- * Entity Handling (selectable) -- -- Limitations: -- ============ -- -- * Non-validating -- * No charset handling -- * No namespace support -- * Shallow well-formedness checking only (fails -- to detect most semantic errors) -- -- API: -- ==== -- -- The parser provides a partially object-oriented API with -- functionality split into tokeniser and handler components. -- -- The handler instance is passed to the tokeniser and receives -- callbacks for each XML element processed (if a suitable handler -- function is defined). The API is conceptually similar to the -- SAX API but implemented differently. -- -- XML data is passed to the parser instance through the 'parse' -- method (Note: must be passed a single string currently) -- -- License: -- ======== -- -- This code is freely distributable under the terms of the [MIT license](LICENSE). -- -- --@author Paul Chakravarti (paulc@passtheaardvark.com) --@author Manoel Campos da Silva Filho local xml2lua = { _VERSION = "1.6-1" } local XmlParser = require("XmlParser") ---Recursivelly prints a table in an easy-to-ready format --@param tb The table to be printed --@param level the indentation level to start with local function printableInternal(tb, level) if tb == nil then return end level = level or 1 local spaces = string.rep(' ', level*2) for k,v in pairs(tb) do if type(v) == "table" then print(spaces .. k) printableInternal(v, level+1) else print(spaces .. k..'='..v) end end end ---Instantiates a XmlParser object to parse a XML string --@param handler Handler module to be used to convert the XML string --to another formats. See the available handlers at the handler directory. -- Usually you get an instance to a handler module using, for instance: -- local handler = require("xmlhandler/tree"). --@return a XmlParser object used to parse the XML --@see XmlParser function xml2lua.parser(handler) if handler == xml2lua then error("You must call xml2lua.parse(handler) instead of xml2lua:parse(handler)") end local options = { --Indicates if whitespaces should be striped or not stripWS = 1, expandEntities = 1, errorHandler = function(errMsg, pos) error(string.format("%s [char=%d]\n", errMsg or "Parse Error", pos)) end } return XmlParser.new(handler, options) end ---Recursivelly prints a table in an easy-to-ready format --@param tb The table to be printed function xml2lua.printable(tb) printableInternal(tb) end ---Handler to generate a string prepresentation of a table --Convenience function for printHandler (Does not support recursive tables). --@param t Table to be parsed --@return a string representation of the table function xml2lua.toString(t) local sep = '' local res = '' if type(t) ~= 'table' then return t end for k,v in pairs(t) do if type(v) == 'table' then v = xml2lua.toString(v) end res = res .. sep .. string.format("%s=%s", k, v) sep = ',' end res = '{'..res..'}' return res end --- Loads an XML file from a specified path -- @param xmlFilePath the path for the XML file to load -- @return the XML loaded file content function xml2lua.loadFile(xmlFilePath) local f, e = io.open(xmlFilePath, "r") if f then --Gets the entire file content and stores into a string local content = f:read("*a") f:close() return content end error(e) end ---Gets an _attr element from a table that represents the attributes of an XML tag, --and generates a XML String representing the attibutes to be inserted --into the openning tag of the XML -- --@param attrTable table from where the _attr field will be got --@return a XML String representation of the tag attributes local function attrToXml(attrTable) local s = "" attrTable = attrTable or {} for k, v in pairs(attrTable) do s = s .. " " .. k .. "=" .. '"' .. v .. '"' end return s end ---Gets the first key of a given table local function getSingleChild(tb) local count = 0 for _ in pairs(tb) do count = count + 1 end if (count == 1) then for k, _ in pairs(tb) do return k end end return nil end ---Gets the first value of a given table local function getFirstValue(tb) if type(tb) == "table" then for _, v in pairs(tb) do return v end return nil end return tb end xml2lua.pretty = true function xml2lua.getSpaces(level) local spaces = '' if (xml2lua.pretty) then spaces = string.rep(' ', level * 2) end return spaces end function xml2lua.addTagValueAttr(tagName, tagValue, attrTable, level) local attrStr = attrToXml(attrTable) local spaces = xml2lua.getSpaces(level) if (tagValue == '') then table.insert(xml2lua.xmltb, spaces .. '<' .. tagName .. attrStr .. '/>') else table.insert(xml2lua.xmltb, spaces .. '<' .. tagName .. attrStr .. '>' .. tostring(tagValue) .. '') end end function xml2lua.startTag(tagName, attrTable, level) local attrStr = attrToXml(attrTable) local spaces = xml2lua.getSpaces(level) if (tagName ~= nil) then table.insert(xml2lua.xmltb, spaces .. '<' .. tagName .. attrStr .. '>') end end function xml2lua.endTag(tagName, level) local spaces = xml2lua.getSpaces(level) if (tagName ~= nil) then table.insert(xml2lua.xmltb, spaces .. '') end end function xml2lua.isChildArray(obj) for tag, _ in pairs(obj) do if (type(tag) == 'number') then return true end end return false end function xml2lua.isTableEmpty(obj) for k, _ in pairs(obj) do if (k ~= '_attr') then return false end end return true end function xml2lua.parseTableToXml(obj, tagName, level) if (tagName ~= '_attr') then if (type(obj) == 'table') then if (xml2lua.isChildArray(obj)) then for _, value in pairs(obj) do xml2lua.parseTableToXml(value, tagName, level) end elseif xml2lua.isTableEmpty(obj) then xml2lua.addTagValueAttr(tagName, "", obj._attr, level) else xml2lua.startTag(tagName, obj._attr, level) for tag, value in pairs(obj) do xml2lua.parseTableToXml(value, tag, level + 1) end xml2lua.endTag(tagName, level) end else xml2lua.addTagValueAttr(tagName, obj, nil, level) end end end ---Converts a Lua table to a XML String representation. --@param tb Table to be converted to XML --@param tableName Name of the table variable given to this function, -- to be used as the root tag. If a value is not provided -- no root tag will be created. --@param level Only used internally, when the function is called recursively to print indentation -- --@return a String representing the table content in XML function xml2lua.toXml(tb, tableName, level) xml2lua.xmltb = {} level = level or 0 local singleChild = getSingleChild(tb) tableName = tableName or singleChild if (singleChild) then xml2lua.parseTableToXml(getFirstValue(tb), tableName, level) else xml2lua.parseTableToXml(tb, tableName, level) end if (xml2lua.pretty) then return table.concat(xml2lua.xmltb, '\n') end return table.concat(xml2lua.xmltb) end return xml2lua