--[[-- A module that maps between Lua and XML without much ado. LuaXML provides a set of functions for processing XML data in Lua. It offers a very simple and natural mapping between the XML format and Lua tables, which allows one to work with and construct XML data just using Lua's normal table access and iteration methods (e.g. `ipairs()`). Substatements and tag content are represented as array data having numerical keys (`1 .. n`), attributes use string keys, and tags the numerical index `0`. This representation makes sure that the structure of XML data is preserved exactly across read/write cycles (i.e. `xml.eval(var:str())` should equal `var` again). ---
To use LuaXML, first import the module - for example like this: local xml = require("LuaXML") LuaXML consists of a Lua file (`LuaXML.lua`) and a corresponding C module (`LuaXML_lib`) - normally a shared library (`.dll`/`.so`), although a static linking is possible as well. Both parts are imported by this call, provided that they are found in Lua's package search path.   @module LuaXML ]] local _M = require("LuaXML_lib") --[[-- saves a Lua var as XML file. Basically this simply exports the string representation `xml.str(var)` (or `var:str()`), plus a standard header. @function save @param var the variable to be saved, normally a table @tparam string filename the filename to be used. An existing file of the same name gets overwritten. @tparam ?string filemode the file mode to pass to `io.open`, defaults to "w" @tparam ?string cmt custom _comment_ to be written at the top of the file (after the header). You may pass an empty string if you don't want any comment at all, otherwise it should preferably end with at least one newline. Defaults to: \n\n @tparam ?string hdr custom _header_, written before anything else. You may pass an empty string if you don't want any header at all, otherwise it should preferably end with a newline. Defaults to the standard XML 1.0 declaration: \n @usage var:save("simple.xml") var:save("no-comment.xml", nil, "") var:save("custom.xml", "a+", "\n", "") ]] function _M.save(var, filename, filemode, comment, header) if var and filename and #filename > 0 then local file, err = io.open(filename, filemode or "w") if not file then error('error opening "' .. filename .. '" for saving: ' .. err) end file:write(header or '\n') file:write(comment or '\n\n') file:write(_M.str(var)) file:close() end end --[[-- iterate subelements ("XML children") as _key - value_ pairs. This function is meant to be called in a generic `for` loop, similar to what `ipairs(var)` would do. However you can easily specify additional criteria to `match` against here, possibly reducing the overhead needed to test for specific subelements. For the resulting `(k, v)` pairs, note that `k` is just a sequential number in the array of matched child elements, and has no direct relation to the actual "position" (subtag index) within each `v`'s parent object. @function children @param var the table (LuaXML object) to work on @tparam ?string tag XML tag to be matched @tparam ?string key attribute key to be matched @param value (optional) attribute value to be matched @tparam ?number maxdepth maximum depth allowed, defaults to 1 (only immediate children). You can pass 0 or `true` to iterate _all_ children recursively. @return Lua iterator function and initial state (Lua-internal use) - suitable for a `for` loop @see match @usage local xml = require("LuaXML") local foobar = xml.eval('') -- iterate over those children that have a "bar" attribute: for k, v in foobar:children(nil, "bar") do print(k, v:tag(), v.bar) end -- will print -- 1 b no -- 2 c yes -- require "bar" to be "yes": for k, v in foobar:children(nil, "bar", "yes") do print(k, v:tag(), v.bar) end -- will print -- 1 c yes -- iterate "a" tags: (the first and fourth child will match) for k, v in foobar:children("a") do print(k, v:tag(), v.bar) end -- will print -- 1 a nil -- 2 a nil ]] function _M.children(var, tag, key, value, maxdepth) local function get_children(var, tag, key, value, maxdepth) -- pass maxdepth = 1 to retrieve only immediate child nodes local result = {} _M.iterate(var, function(node, depth) -- add matching node to result table if depth > 0 then table.insert(result, node); end end, tag, key, value, true, maxdepth) return result end local function child_iterator(matched, k) k = (k or 0) + 1 local v = matched[k] return v and k, v -- key/value pair from matches, or `nil` if no value end maxdepth = maxdepth or 1 -- default to 1... -- ...but enumerate all children if it was set to 0 or `true` if maxdepth == 0 or maxdepth == true then maxdepth = nil; end -- our "invariant state" will be a table of matched children return child_iterator, get_children(var, tag, key, value, maxdepth) end return _M -- return module (table)