Output Lua Table To Html File


The following sample code converts a Lua table into an HTML file that can be viewed by a web browser. Different levels of the table nesting are rendered with different colors. The maximum level of nesting is set to 10, but this can be easily tweaked for higher levels.

-- dontspamme_samlie@yahoo.com

-- Converts Lua table to HTML output in table.html file
function tohtml(x)
 ret = tohtml_table(x,1)
 writefile("table.html", ret)
 os.execute("table.html")
 return(ret)
end
-- Saves a string to file
function writefile(filename, value)
 if (value) then
 local file = io.open(filename,"w+")
 file:write(value)
 file:close()
 end
end
-- Flattens a table to html output
function tohtml_table(x, table_level)
 local k, s, tcolor
 local html_colors = {
 "#339900","#33CC00","#669900","#666600","#FF3300",
 "#FFCC00","#FFFF00","#CCFFCC","#CCCCFF","#CC66FF",
 "#339900","#33CC00","#669900","#666600","#FF3300",
 "#FFCC00","#FFFF00","#CCFFCC","#CCCCFF","#CC66FF"
 }
 local lineout = {}
 local tablefound = false
 if type(x) == "table" then
 s = ""
 k = 1
 local i, v = next(x)
 while i do
 if (type(v) == "table") then
 if (table_level<10) then
 lineout[k] = "<b>" .. flat(i) .. "</b>".. tohtml_table(v, table_level + 1) 
 else
 lineout[k] = "<b>MAXIMUM LEVEL BREACHED</b>"
 end
 tablefound = true
 else
 lineout[k] = flat(i) .. "=" .. tohtml_table(v)
 end
 k = k + 1
 i, v = next(x, i)
 end
 for k,line in ipairs(lineout) do
 if (tablefound) then
 s = s .. "<tr><td>" .. line .. "</td></tr>\n"
 else
 s = s .. "<td>" .. line .. "</td>\n"
 end
 end
 if not (tablefound) then
 s = "<table border='1' bgcolor='#FFFFCC' cellpadding='5' cellspacing='0'>" ..
 "<tr>" .. s .. "</tr></table>\n"
 else
 tcolor = html_colors[table_level]
 s = "<table border='3' bgcolor='"..tcolor.."' cellpadding='10' cellspacing='0'>" ..
 s .. "</table>\n"
 end
 return s 
 end
 if type(x) == "function" then
 return "FUNC"
 end
 if type(x) == "file" then
 return "FILE"
 end
 return tostring(x) 
end
-- Flattens a table to string
function flat(x) 
 return toflat(x,1)
end
-- Flattens a table to string
function toflat(x, tlevel)
 local s
 tlevel = tlevel + 1
 if type(x) == "table" then
 s = "{"
 local i, v = next(x)
 while i do
 if (tlevel < 15) then
 s = s .. i .. "=" .. toflat(v, tlevel) 
 else
 s = s .. i .. "={#}" 
 end
 i, v = next(x, i)
 if i then
 s = s .. ", " 
 end
 end
 return s .. "}\n"
 end
 if type(x) == "function" then
 return "FUNC"
 end
 if type(x) == "file" then
 return "FILE"
 end
 return tostring(x) 
end

Take #2

Here is a more advanced implementation, with expand/collapsing and handling cycles. (See warning in comments before use.)

-- Example dumping Lua tables to HTML for debugging.
--
-- Warning: not complete or well tested. This is only intended
-- as an example/starting point. Clean it up if used in production.
--
-- (c) 2008 David Manura (2008-12)
-- Licensed under the same terms as Lua (MIT license).
local coroutine = coroutine
local next = next
local pairs = pairs
local string = string
local tostring = tostring
local type = type
local _G = _G
local format = string.format
-- Escape string to make suitable for embedding in HTML.
local function htmlize(s)
 s = s:gsub('&', '&amp;')
 s = s:gsub('<', '&lt;')
 s = s:gsub('>', '&gt;')
 return s
end
-- iterator function for table pairs.
-- hash part, then array part.
-- used for display.
local function table_pairs(t)
 local keys = {}
 for k in pairs(t) do keys[#keys+1] = k end
 table.sort(keys, function(a,b)
 if type(a) == 'number' and type(b) == 'string' then
 return false
 elseif type(a) == 'string' and type(b) == 'number' then
 return true
 else
 return a < b
 end
 end)
 local i = 0
 return function()
 i = i + 1
 local k = keys[i]
 if k then return k, t[k] end
 end
end
-- Serialize object o. Writes one or more substrings to function append.
local function obj_serialize(o, append)
 if type(o) == 'table' then
 append('{')
 for k,v in table_pairs(o) do
 append('[')
 obj_serialize(k, append)
 append(']=[')
 obj_serialize(v, append)
 append('];')
 end
 append('}')
 elseif type(o) == 'string' then
 append(string.format('%q', o))
 else
 append(tostring(o))
 end
end
-- Returns serialization of o, not exceeding maxlen characters.
local function obj_tostring_short(o, maxlen)
 local s = ''
 local function append(ss)
 s = s .. ss
 if #s > maxlen then
 s = s:sub(1,maxlen) .. '...'
 coroutine.yield()
 end
 end
 local f = coroutine.wrap(obj_serialize)
 f(o, append)
 return s
end
local function analyze_tree(o)
 local ids = {}
 local current_id = 0
 local count = {}
 local from = {}
 local function analyze_tree_helper(o)
 if type(o) == 'table' then
 if count[o] then
 count[o] = count[o] + 1
 else
 count[o] = 1
 current_id = current_id + 1
 ids[o] = current_id
 
 local this_id = current_id
 for k,v in pairs(o) do
 analyze_tree_helper(k)
 analyze_tree_helper(v)
 if type(k) == 'table' then
 from[k] = from[k] or {}; from[k][o] = this_id .. '.' .. ids[k]
 end
 if type(v) == 'table' then
 from[v] = from[v] or {};
 from[v][o] = this_id .. '.' .. (ids[k] or tostring(k))
 end
 end
 end
 end
 end
 analyze_tree_helper(o)
 for k,v in pairs(count) do
 if v == 1 then count[k] = nil end
 end
 return ids, count, from
end
local header = [[
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
<title>FIX</title>
<style type="text/css">
.table {margin-left:1em; border: 1px solid black}
.table_row {border: 1px solid black}
</style>
<script type="text/javascript"><!--
function toggle(id) {
 if (document.getElementById) {
 var ele = document.getElementById(id);
 if (ele && ele.style) {
 ele.style.display = ele.style.display == 'none' ? '' : 'none';
 }
 }
}
function show_node(ele) {
 if (ele.style) {
 ele.style.display = '';
 if (ele.parentNode) show_node(ele.parentNode);
 }
}
function show(id) {
 if (document.getElementById) {
 var ele = document.getElementById(id);
 if (ele) show_node(ele);
 }
}
//--></script>
</head>
<body>
]]
local footer = [[</body></html>]]
-- Writes HTML representations of object o as one or more strings to
-- function output.
local function object_to_html(o, output)
 local ids, count, from = analyze_tree(o)
 local output_html
 local function output_header_html(o)
 if type(o) == 'table' then
 output(format('<a name="id%s"></a>', ids[o]))
 local is_empty = next(o) == nil
 output(is_empty and '(empty)' or
 '<a href="javascript:toggle(\'id' .. ids[o] .. '\')">[+]</a>')
 output(format([[Table ID %s]], ids[o]))
 output(type(o.tag) == 'string' and ' [Tag=' .. o.tag .. ']' or '')
 output(' ')
 output(htmlize(obj_tostring_short(o, 40)))
 elseif type(o) == 'string' then
 output(htmlize(string.format('%q', o)))
 else
 output(htmlize(tostring(o)))
 end 
 end
 local function output_body_html(o)
 if type(o) == 'table' then
 output(format('<div class="table" style="display:none" id="id%s">\n', ids[o]))
 -- xref
 if from[o] and next(from[o]) and next(from[o], next(from[o])) then
 output('<div>Referenced from: ')
 for _,from_id in pairs(from[o]) do
 output(format([[ <a onclick="show('id%s')" href="#id%s">%s</a> ]],
 from_id, from_id, from_id))
 end
 output('</div>')
 end
 -- key/values
 for k,v in table_pairs(o) do
 local function prepare_output(oo)
 local f, is_long
 if count[oo] then
 f = function()
 output(format([[<a onclick="show('id%s')" href="#id%s">(see %s)</a>]],
 ids[oo],ids[oo],ids[oo]))
 end
 elseif type(oo) ~= 'table' then
 f = function() output_header_html(oo) end
 else
 f = function() output_header_html(oo) end
 is_long = true
 end
 return f, is_long
 end
 local kf, klong = prepare_output(k)
 local vf, vlong = prepare_output(v)
 local field_id = ids[o] .. '.' .. (ids[k] or tostring(k))
 output(format([[<a name="id%s"></a>]], field_id))
 output(format('<div class="table_row" id="id%s">\n', field_id))
 kf()
 output('=')
 vf()
 if vlong then output_body_html(v) end
 output('</div>')
 end
 output('</div>\n')
 end
 end
 --local
 function output_html(o)
 output('<div>')
 output_header_html(o)
 output('</div>')
 output_body_html(o)
 end
 output(header)
 output_html(o)
 for oo in pairs(count) do
 if oo ~= o then
 output_html(oo)
 end
 end
 output(footer)
end
-- example usage
local function dump(obj)
 object_to_html(obj, function(s) io.stdout:write(s) end)
end
dump(_G)
-- metalua example
--[[
package.path = package.path .. ';/luaanalyze/lib/?.lua'
require "lexer"
require "gg"
require "mlp_lexer"
require "mlp_misc"
require "mlp_table"
require "mlp_meta"
require "mlp_expr"
require "mlp_stat"
require "mlp_ext"
mlc = {} -- make gg happy
local mlp = assert(_G.mlp)
local function string_to_ast(src, filename)
 filename = filename or '(string)'
 local lx = mlp.lexer:newstream (src, filename)
 local ast = mlp.chunk(lx)
 return ast
end
local src_file = assert(io.open ('mydump.lua', 'r'))
local src = src_file:read '*a'; src_file:close()
local ast = string_to_ast(src, src_filename)
local function remove_lineinfo(o)
 if type(o) == 'table' then
 o.lineinfo = nil
 for k,v in pairs(o) do
 remove_lineinfo(k)
 remove_lineinfo(v)
 end
 end
end
remove_lineinfo(ast)
dump(ast)
--]]

See Also


RecentChanges · preferences
edit · history
Last edited June 2, 2020 9:21 am GMT (diff)

AltStyle によって変換されたページ (->オリジナル) /