123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154 |
- --[[--
- 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).
- ---
- <br>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:
- <!-- file «filename», generated by LuaXML -->\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:
- <?xml version="1.0"?>\n
- @usage
- var:save("simple.xml")
- var:save("no-comment.xml", nil, "")
- var:save("custom.xml", "a+", "<!-- append mode, no header -->\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 '<?xml version="1.0"?>\n')
- file:write(comment or
- '<!-- file "' .. filename .. '", generated by LuaXML -->\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('<foo><a /><b bar="no" /><c bar="yes" /><a /></foo>')
- -- 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)
|