LuaXML.lua 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. --[[--
  2. A module that maps between Lua and XML without much ado.
  3. LuaXML provides a set of functions for processing XML data in Lua.
  4. It offers a very simple and natural mapping between the XML format and Lua tables,
  5. which allows one to work with and construct XML data just using Lua's normal
  6. table access and iteration methods (e.g. `ipairs()`).
  7. Substatements and tag content are represented as array data having numerical
  8. keys (`1 .. n`), attributes use string keys, and tags the numerical index `0`.
  9. This representation makes sure that the structure of XML data is preserved
  10. exactly across read/write cycles (i.e. `xml.eval(var:str())` should equal `var`
  11. again).
  12. ---
  13. <br>To use LuaXML, first import the module - for example like this:
  14. local xml = require("LuaXML")
  15. LuaXML consists of a Lua file (`LuaXML.lua`) and a corresponding C module
  16. (`LuaXML_lib`) - normally a shared library (`.dll`/`.so`), although a static
  17. linking is possible as well. Both parts are imported by this call, provided
  18. that they are found in Lua's package search path.
  19. &nbsp;
  20. @module LuaXML
  21. ]]
  22. local _M = require("LuaXML_lib")
  23. --[[-- saves a Lua var as XML file.
  24. Basically this simply exports the string representation `xml.str(var)`
  25. (or `var:str()`), plus a standard header.
  26. @function save
  27. @param var the variable to be saved, normally a table
  28. @tparam string filename the filename to be used. An existing file of the same name gets overwritten.
  29. @tparam ?string filemode the file mode to pass to `io.open`, defaults to "w"
  30. @tparam ?string cmt
  31. custom _comment_ to be written at the top of the file (after the header). You
  32. may pass an empty string if you don't want any comment at all, otherwise it
  33. should preferably end with at least one newline. Defaults to:
  34. <!-- file «filename», generated by LuaXML -->\n\n
  35. @tparam ?string hdr
  36. custom _header_, written before anything else. You may pass an empty string if
  37. you don't want any header at all, otherwise it should preferably end with a
  38. newline. Defaults to the standard XML 1.0 declaration:
  39. <?xml version="1.0"?>\n
  40. @usage
  41. var:save("simple.xml")
  42. var:save("no-comment.xml", nil, "")
  43. var:save("custom.xml", "a+", "<!-- append mode, no header -->\n", "")
  44. ]]
  45. function _M.save(var, filename, filemode, comment, header)
  46. if var and filename and #filename > 0 then
  47. local file, err = io.open(filename, filemode or "w")
  48. if not file then
  49. error('error opening "' .. filename .. '" for saving: ' .. err)
  50. end
  51. file:write(header or '<?xml version="1.0"?>\n')
  52. file:write(comment or
  53. '<!-- file "' .. filename .. '", generated by LuaXML -->\n\n')
  54. file:write(_M.str(var))
  55. file:close()
  56. end
  57. end
  58. --[[-- iterate subelements ("XML children") as _key - value_ pairs.
  59. This function is meant to be called in a generic `for` loop, similar to what
  60. `ipairs(var)` would do. However you can easily specify additional criteria
  61. to `match` against here, possibly reducing the overhead needed to test for
  62. specific subelements.
  63. For the resulting `(k, v)` pairs, note that `k` is just a sequential number
  64. in the array of matched child elements, and has no direct relation to the
  65. actual "position" (subtag index) within each `v`'s parent object.
  66. @function children
  67. @param var the table (LuaXML object) to work on
  68. @tparam ?string tag XML tag to be matched
  69. @tparam ?string key attribute key to be matched
  70. @param value (optional) attribute value to be matched
  71. @tparam ?number maxdepth
  72. maximum depth allowed, defaults to 1 (only immediate children).
  73. You can pass 0 or `true` to iterate _all_ children recursively.
  74. @return Lua iterator function and initial state (Lua-internal use) - suitable
  75. for a `for` loop
  76. @see match
  77. @usage
  78. local xml = require("LuaXML")
  79. local foobar = xml.eval('<foo><a /><b bar="no" /><c bar="yes" /><a /></foo>')
  80. -- iterate over those children that have a "bar" attribute:
  81. for k, v in foobar:children(nil, "bar") do
  82. print(k, v:tag(), v.bar)
  83. end
  84. -- will print
  85. -- 1 b no
  86. -- 2 c yes
  87. -- require "bar" to be "yes":
  88. for k, v in foobar:children(nil, "bar", "yes") do
  89. print(k, v:tag(), v.bar)
  90. end
  91. -- will print
  92. -- 1 c yes
  93. -- iterate "a" tags: (the first and fourth child will match)
  94. for k, v in foobar:children("a") do
  95. print(k, v:tag(), v.bar)
  96. end
  97. -- will print
  98. -- 1 a nil
  99. -- 2 a nil
  100. ]]
  101. function _M.children(var, tag, key, value, maxdepth)
  102. local function get_children(var, tag, key, value, maxdepth)
  103. -- pass maxdepth = 1 to retrieve only immediate child nodes
  104. local result = {}
  105. _M.iterate(var,
  106. function(node, depth)
  107. -- add matching node to result table
  108. if depth > 0 then table.insert(result, node); end
  109. end,
  110. tag, key, value, true, maxdepth)
  111. return result
  112. end
  113. local function child_iterator(matched, k)
  114. k = (k or 0) + 1
  115. local v = matched[k]
  116. return v and k, v -- key/value pair from matches, or `nil` if no value
  117. end
  118. maxdepth = maxdepth or 1 -- default to 1...
  119. -- ...but enumerate all children if it was set to 0 or `true`
  120. if maxdepth == 0 or maxdepth == true then maxdepth = nil; end
  121. -- our "invariant state" will be a table of matched children
  122. return child_iterator,
  123. get_children(var, tag, key, value, maxdepth)
  124. end
  125. return _M -- return module (table)