查看“模块:SpecialWikitext/JSON”的源代码
←
模块:SpecialWikitext/JSON
跳转至:
导航
、
搜索
因为以下原因,您没有权限编辑本页:
您所请求的操作仅限于该用户组的用户使用:
用户
您可以查看与复制此页面的源代码。
local p={} local lib_arg={} -- ======== 前處理器 ======== --由於技術限制,lua無法從mw.text.jsonDecode的回傳結果判斷物件、陣列與null因此定義一個$轉義符號來處理 function _preEsc(str) return mw.ustring.gsub(str,'%$',"$$") end function _postEsc(str) if not mw.ustring.find(tostring(str),'%$') then return str end return mw.ustring.gsub(str,'%$%$',"$") end --處理HTML轉義符號 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 --標記null物件 function _handleNull(input_string) local JSON_syms = ",:%[%]%{%}" local head_patterns,tail_patterns = {'^(%s*)','(['..JSON_syms..']%s*)'},{'(%s*)$','(%s*['..JSON_syms..'])'} local result = input_string for i=1,#head_patterns do for j=1,#tail_patterns do result = mw.ustring.gsub(result,head_patterns[i].."null"..tail_patterns[j],function(head,tail) local p_head,p_tail = mw.text.trim(head),mw.text.trim(tail) local body = (p_tail==':'and''or'$').."null" return head..'"'..body..'"'..tail end) end end return result end --標記陣列物件,令其與object物件區別 function _handleArray(input_string) local result = input_string result = mw.ustring.gsub(result,'%[','["$array",') result = mw.ustring.gsub(result,",%s*([%]%}])","%1") return result end --將字串物件轉義,避免_handleArray過度標記 function _handleEscape(json_str) local result, json_stack = {}, {} local result_str = json_str xpcall(function() json_stack[#json_stack + 1]=mw.text.jsonDecode(json_str, mw.text.JSON_PRESERVE_KEYS + mw.text.JSON_TRY_FIXING) while(#json_stack > 0)do local top = json_stack[#json_stack] json_stack[#json_stack] = nil--pop if type(top) == type({}) then for k,v in pairs(top) do if type(k) == type('') then local esc_res = _wikiescape(k) if esc_res ~= k then result[#result + 1] = {k, _wikiescape(k)} end end json_stack[#json_stack + 1] = v end elseif type(top) == type('') then local esc_res = _wikiescape(top) if esc_res ~= top then result[#result + 1] = {top, _wikiescape(top)} end end end for i=1,#result do local pattern = '([\'"])'..mw.ustring.gsub(result[i][1],'([%%%(%)%[%]%{%}%|%$%^%+%-%*%.%?])','%%%1')..'([\'"])' local replace_to = '%1'..mw.ustring.gsub(result[i][2],'%%','%%%%')..'%2' result_str = mw.ustring.gsub(result_str, pattern, replace_to) end end,function() result = true end) if type(result) == type(true) then return result_str end return result_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 _rendeRawString(value) return _renderHide('"')..value.._renderHide('"') end function _rendeValue(value) if _isNumber(value) then return tostring(value) elseif type(value)==type(true) then return value and 'true' or 'false' elseif mw.ustring.find(value,"^%$null") then return 'null' elseif mw.ustring.find(value,"^%$array") then return _rendeRawString('<div class="mw-json-empty"><span class="mw-json-empty-array"></span></div>') elseif mw.ustring.find(value,"^%$reference") then local s_strat, s_end = mw.ustring.find(value,":") local path = s_strat and mw.ustring.sub(value,s_strat+1,-1) return _rendeRawString('<div class="mw-json-reference"><span class="mw-json-reference-tag">'.. (path and('[[#mw-json-ref-'..path..'|'..path..']]')or'')..'</span></div>') elseif mw.ustring.find(value,"^%$function") then local s_strat, s_end = mw.ustring.find(value,":") return _rendeRawString('<div class="mw-json-function"><span class="mw-json-function-tag">'.. (s_strat and mw.ustring.sub(value,s_strat+1,-1) or '')..'</span></div>') elseif mw.ustring.find(value,"^%$error") then local s_strat, s_end = mw.ustring.find(value,":") return _rendeRawString('<div class="mw-json-error"><strong class="error mw-json-error-tag">'.. (s_strat and mw.ustring.sub(value,s_strat+1,-1) or '')..'</strong></div>') end return _rendeString(_postEsc(value)) end function _renderTerminal(key, value, is_array, is_tail, sing_value) local body = '<tr>' ..((key and not is_array) and ('<th><span>'.._renderKey(key)..'</span></th>') or '') ..'<td'..(sing_value and''or' 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) if (tostring(obj):lower():match("nan$"))=='nan' then return false end 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 if (obj[0] or '') == '$array' then return true 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 _jsonDecode(str) local preJSON = _preEsc(str) preJSON = _handleEscape(preJSON) preJSON = _handleNull(preJSON) preJSON = _handleArray(preJSON) preJSON = mw.text.decode(preJSON) return mw.text.jsonDecode(preJSON, mw.text.JSON_PRESERVE_KEYS + mw.text.JSON_TRY_FIXING) 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(_jsonDecode(json_data)) end,function(ex) local check_str = mw.ustring.gsub(json_data,"[\t\r\n\f%s,]+","") local regex_check, show_code = mw.ustring.gsub(check_str,"^([%{%[].).*(.[%}%]])$","%1%2") show_code = show_code > 0 if mw.ustring.sub(regex_check,1,2)=="{{" then show_code = false end--物件中沒有key直接是物件絕對非法 if show_code then mw.addWarning("{{error|JSON解析器回傳錯誤:"..ex.."}}") body = mw.getCurrentFrame():extensionTag{ name = 'syntaxhighlight', content = json_data, args = { lang = 'json', line = 'line' } } else --明顯非法的json當作一般wikitext mw.addWarning("{{error|錯誤,輸入的資料並非JSON}},將以普通wikitext呈現。") body = mw.getCurrentFrame():preprocess(mw.text.decode(json_data)) end end) return body end --{{#invoke:SpecialWikitext/JSON|callAsJSON}} function p.callAsJSON(...) local f_args = { ... } local in_args, working_frame local frame = f_args[1] if frame == mw.getCurrentFrame() then -- We're being called via #invoke. The args are passed through to the module -- from the template page, so use the args that were passed into the template. if lib_arg.getArgs == nil then lib_arg = require('Module:Arguments') end in_args = lib_arg.getArgs(frame, {parentFirst=true}) working_frame = frame else -- We're being called from another module or from the debug console, so assume -- the args are passed in directly. in_args = frame working_frame = mw.getCurrentFrame() if type(in_args) ~= type({}) then in_args = {} for i=1,#f_args do in_args[i] = f_args[i] end end end local args = {} for k,v in pairs(in_args) do args[k]=v end local json_data, flag = mw.text.killMarkers( mw.text.unstripNoWiki( tostring(args[1] or args['1'] or '') ) ) local path = mw.text.split(json_data,'%.') if #path < 1 then return '' end local obj = _G[path[1]] if obj == nil then local file = mw.title.new(path[1], 'module') if file.exists then local contentModel = file.contentModel if contentModel == 'scribunto' or contentModel == 'lua' then flag,obj = pcall(require, file.fullText) elseif contentModel == 'json' then flag,obj = pcall(mw.text.jsonDecode, file:getContent()) end end end if obj == nil then return '' end local get_arg = function(arg_list) local result_args = {} for j=2,#arg_list do local check_key = mw.text.trim(arg_list[j]) if mw.ustring.sub(check_key,1,1)=='$' then local str_res,is_str = mw.ustring.gsub(check_key,"^%$string",'') local num_res,is_num = mw.ustring.gsub(check_key,"^%$number",'') if is_str > 0 then result_args[#result_args+1] = str_res elseif is_num > 0 then num_res = mw.ustring.lower(mw.text.trim(num_res)) if num_res == 'true' then result_args[#result_args+1] = true elseif num_res == 'false' then result_args[#result_args+1] = false elseif tonumber(num_res)~=nil then result_args[#result_args+1] = tonumber(num_res) else result_args[#result_args+1] = num_res end end else result_args[#result_args+1] = arg_list[j] end end return result_args end local target_obj = obj local old_obj = _G; local cur_path = path[1] local tmp_args = {} local no_call = false for i=2,#path do if i~=#path or path[i]~=':' then if target_obj==nil then return p._json_reader('$error: "'..cur_path..'" is nil.') end local old_path = cur_path cur_path = old_path ..'.'.. path[i] if type(target_obj)==type(function()end) then local success_obj flag,success_obj = pcall(target_obj, unpack(tmp_args)) if not success_obj or not flag then --try colon call flag,target_obj = pcall(target_obj, old_obj, unpack(tmp_args)) if not target_obj or not flag then return p._json_reader('$error: fail to get "'..cur_path..'" .') end else target_obj = success_obj end elseif type(target_obj)~=type({}) then return p._json_reader('$error: can not get "'..cur_path..'" from '..tostring(type(target_obj))..' value ('..old_path..').') end local cur_key = mw.text.split(path[i],':') tmp_args = get_arg(cur_key) local cur_obj_key = mw.text.trim(cur_key[1]) if type(target_obj[cur_obj_key])~=type(function()end) then cur_obj_key = path[i] end old_obj = target_obj target_obj = target_obj[cur_obj_key] else no_call = true end end local pack_data = {true,target_obj} if type(target_obj)==type(function()end) and not no_call then tmp_args = get_arg(args) pack_data = {pcall(target_obj, unpack(tmp_args))} local err_flag = pack_data[1] if not err_flag then local success_obj --try colon call success_obj = {pcall(target_obj, old_obj, unpack(tmp_args))} if not success_obj[1] then return p._json_reader('$error: fail to call "'..cur_path..'" '..(pack_data[2] and (', Message: '..pack_data[2]) or '')..'.') else pack_data = success_obj end end end local body_list = {} for i=2,#pack_data do local jsonlize local reference_list={} local target_unpack = pack_data[i] if target_unpack == nil then jsonlize = '$null' elseif type(target_unpack)==type(function()end) then jsonlize = '$function:'..cur_path elseif type(target_unpack)~=type({}) then jsonlize = _preEsc(tostring(target_unpack)) else jsonlize,reference_list = p._table_copy(target_unpack) end body_list[#body_list+1]=p._json_reader(jsonlize, reference_list, (#pack_data>2)and'mw-json-multivalue'or nil) end if #body_list == 0 then return p._json_reader('$null') elseif #body_list == 1 then return body_list[1] else local body = '' for i=1,#body_list do if body ~= '' then body=body..'\n' end body = body..body_list[i] end return '<table class="mw-json-multiple"><tr><td>'.. body..'</td></tr></table>' end end function p._table_copy(json,target) local copy = {result=target or {}} local check_deep = {} local reference_list = {} local json_stack={{obj=json,target=copy,key='result',path='root'}} while(#json_stack > 0)do local top = json_stack[#json_stack] json_stack[#json_stack] = nil--pop local topkey = _preEsc(top.key) if _isNumber(top.key) then topkey = tonumber(top.key) end if _isNumber(top.key) or type(top.key) == type('') then if type(top.obj) == type({}) then local is_same = false local cur_data for i=1,#check_deep do if check_deep[i].obj == top.obj then is_same = true cur_data = check_deep[i] break end end if not is_same then cur_data = {obj=top.obj, path=top.path..'.'..top.key} check_deep[#check_deep + 1] = cur_data top.target[topkey] = {} if top.obj[0]==nil and _isArray(top.obj)then top.target[topkey][0]="$array" end for k,v in pairs(top.obj) do if type(k) ~= type({}) and type(k) ~= type(function()end) then json_stack[#json_stack + 1]={ obj=v, target=top.target[topkey], key=_isNumber(k)and tonumber(k)or tostring(k), path=top.path..'.'..top.key } end end else local ref_path = mw.ustring.gsub(cur_data.path,'^root%.result%.','') top.target[topkey] = '$reference:'..ref_path reference_list[ref_path] = true end elseif type(top.obj) == type(function()end) then top.target[topkey]='$function:'..mw.ustring.gsub(top.path..'.'..top.key,'^root%.result%.','') elseif type(top.obj) == type('') then if _isNumber(top.obj) then top.target[topkey]=tonumber(top.obj) else top.target[topkey]=_preEsc(top.obj) end else top.target[topkey]=top.obj end end end return copy.result,reference_list end function p._json_reader(json,reference_list,other_class) local reference_list = reference_list or {} local sing_value = type(json) == type('') or type(json) == type(0) or type(json) == type(true) --見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,path='root'} 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', path=top.path..'.'..(top.key or 'result') } 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-array"></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') result_data = _renderItem(_postEsc(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', path=top.path..'.'..(top.key or 'result') } 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 local html_id = nil local ref_path = mw.ustring.gsub(top.path,'^root%.result%.','') if reference_list[ref_path] == true then html_id='mw-json-ref-'..ref_path end if mw.text.trim(top_result) == '' then top_result ='<td class="mw-json-empty"><span class="mw-json-empty-object"></span></td>'end local result_data = _renderHide('{')..'<table class="mw-json"'..(html_id and (' id="'..html_id..'"')or'')..'>'..top_result..'</table>'.._renderHide('}') local is_array = top.key and not (top.type=='array_item') local result_data = _renderItem(_postEsc(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(true) or type(top.obj) == type(''))and _renderTerminal(_postEsc(top.key), top.obj, top.type=='array_item', top.is_last, sing_value) or '' peek.result=top.result..result_data..peek.result end end local class_list = {} if _isArray(json) or sing_value then class_list[#class_list+1]="mw-json" end if sing_value then class_list[#class_list+1]="mw-json-single-value" end if other_class then class_list[#class_list+1]=other_class end class_list = table.concat(class_list, ' ') local outer_class = (mw.text.trim(class_list)~='') and 'class="'..class_list..'"' or '' return '<table '..outer_class..'>' .. json_stack[1].result .. '</table>' end return p
该页面使用的模板:
模块:SpecialWikitext/JSON/doc
(
查看源代码
)
返回至
模块:SpecialWikitext/JSON
。
导航菜单
个人工具
登录
名字空间
模块
讨论
变种
视图
阅读
查看源代码
查看历史
更多
搜索
导航
首页
随机页面
最近更改
最新文件
常用
上传多个文件
工具
链入页面
相关更改
特殊页面
页面信息