模組:Special wikitext/JSON
外观
local p={}
function _wikiescape(str)
local escape_list = {
{'#','#'}, {'%{','{'}, {'%|','|'}, {'%}','}'}, {'=','='}, {':',':'}, {'%[','['}, {'%]',']'}
}
str = mw.text.encode( str )
for i=1,#escape_list do str = mw.ustring.gsub(str,escape_list[i][1],escape_list[i][2]) end
return str
end
function _renderHide(value)
return '<span style="display:none">'..value..'</span>'
end
function _renderKeyString(value)
return _renderHide('"').._wikiescape(value).._renderHide('"')
end
function _renderKey(value)
return _renderKeyString(value).._renderHide(':')
end
function _rendeString(value)
return '"'.._wikiescape(value)..'"'
end
function _rendeValue(value)
if _isNumber(value) then return tostring(value) end
return _rendeString(value)
end
function _renderTerminal(key, value, is_array, is_tail)
local body = '<tr>'
..((key and not is_array) and ('<th><span>'.._renderKey(key)..'</span></th>') or '')
..'<td class="mw-json-value">'.._rendeValue(value)..((not is_tail) and _renderHide(',') or '')..'</td></tr>'
return body
end
function _renderItem(key, item, is_array, is_tail)
local body = '<tr>'
..((key and not is_array) and ('<th><span>'.._renderKey(key)..'</span></th>') or '')
..'<td>'..item..((not is_tail) and _renderHide(',') or '')..'</td></tr>'
return body
end
function _isNumber(obj)
return tonumber(obj) ~= nil and tostring(tonumber(obj)) == tostring(obj)
end
function _isArray(obj)
local min_val, max_val
if obj == nil or type(obj) ~= type({}) then return false end
for k,v in pairs(obj) do
if _isNumber(k) then
local key = tonumber(k)
if min_val==nil then min_val = key end
if max_val==nil then max_val = key end
if min_val > key then min_val = key end
if max_val < key then max_val = key end
else return false end
end
return (min_val or -1) > 0 and #obj==(max_val or -1)
end
function p.JSONTable(frame)
local json_data = mw.text.killMarkers( mw.text.unstripNoWiki( frame.args[1] or frame.args['1'] or '' ) )
local body = ''
xpcall(function()
body=p._json_reader(mw.text.jsonDecode(json_data))
end,function()
body = mw.getCurrentFrame():preprocess(mw.text.decode(json_data))
end)
return body
end
function p._json_reader(json)
--見lua說明,遞迴的變數行為可能會導致遞迴失效,因此用堆疊實現
local json_stack={{result='',obj={}}}
json_stack[#json_stack + 1] = {obj=json,parent=json_stack[1],state='start',root=true,result='',is_last=true}
while(#json_stack > 1)do
local top = json_stack[#json_stack]
if _isArray(top.obj) then
if top.state == 'start'then
top.state = 'process'
top.cap = true
local last_key
for i=1,#top.obj do
last_key = i
json_stack[#json_stack + 1] = {
obj = top.obj[i], key = i, parent=top, state='start',result='',type='array_item'
}
end
if last_key ~= nil then json_stack[#json_stack].is_last = true end
elseif top.state == 'process' then
json_stack[#json_stack] = nil--pop
local peek = top.parent
local top_result = top.result
if mw.text.trim(top_result) == '' then top_result ='<td class="mw-json-empty">空物件</td>'end
local result_data = _renderHide('[')..'<table class="mw-json">'..top_result..'</table>'.. _renderHide(']')
local is_array = top.key and not (top.type=='array_item')
result_data = _renderItem(top.key, result_data, top.type=='array_item', top.is_last)
peek.result=result_data..peek.result
end
elseif type(top.obj) == type({}) then
if top.state == 'start'then
top.state = 'process'
top.cap = true
local last_key
for k,v in pairs(top.obj) do
last_key = k
json_stack[#json_stack + 1] = {
obj = v, key = k, parent=top, state='start',result='',type='obj_item'
}
end
if last_key ~= nil then json_stack[#json_stack].is_last = true end
elseif top.state == 'process' then
json_stack[#json_stack] = nil--pop
local peek = top.parent
local top_result = top.result
if mw.text.trim(top_result) == '' then top_result ='<td class="mw-json-empty"><span class="mw-json-empty-msg"></span></td>'end
local result_data = _renderHide('{')..'<table class="mw-json">'..top_result..'</table>'.._renderHide('}')
local is_array = top.key and not (top.type=='array_item')
local result_data = _renderItem(top.key, result_data, top.type=='array_item', top.is_last)
peek.result=result_data..peek.result
end
else
json_stack[#json_stack] = nil--pop
local peek = top.parent
local result_data = (type(top.obj) == type(0) or type(top.obj) == type(''))and _renderTerminal(top.key, top.obj, top.type=='array_item', top.is_last) or ''
peek.result=top.result..result_data..peek.result
end
end
local outer_class = _isArray(json) and 'class="mw-json"' or ''
return '<table '..outer_class..'>' .. json_stack[1].result .. '</table>'
end
return p