|
|
Zeile 1: |
Zeile 1: |
| local JSONutil = { suite = "JSONutil", | | local JSONutil = { suite = "JSONutil", |
| serial = "2020-11-08", | | serial = "2019-07-18", |
| item = 63869449 } | | item = 63869449 } |
| --[=[ | | --[=[ |
| preprocess or generate JSON data | | preprocess JSON data |
| ]=] | | ]=] |
| | local Failsafe = JSONutil |
|
| |
|
|
| |
|
|
| |
|
| local Failsafe = JSONutil
| |
| JSONutil.Encoder = { stab = string.char( 9 ),
| |
| sep = string.char( 10 ),
| |
| scream = "@error@JSONencoder@" }
| |
| JSONutil.more = 50 -- length of trailing context | | JSONutil.more = 50 -- length of trailing context |
|
| |
|
Zeile 22: |
Zeile 19: |
| :lower() | | :lower() |
| end -- Fallback() | | end -- Fallback() |
|
| |
|
| |
|
| |
| local flat = function ( adjust )
| |
| -- Clean template argument string
| |
| -- Parameter:
| |
| -- adjust -- string, or not
| |
| -- Returns:
| |
| -- string
| |
| local r
| |
| if adjust then
| |
| r = mw.text.trim( mw.text.unstripNoWiki( adjust ) )
| |
| else
| |
| r = ""
| |
| end
| |
| return r
| |
| end -- flat()
| |
|
| |
|
| |
|
| |
| local flip = function ( frame )
| |
| -- Retrieve template argument indent
| |
| -- Parameter:
| |
| -- frame -- object
| |
| -- Returns:
| |
| -- number, of indentation level, or not
| |
| local r
| |
| if frame.args.indent and frame.args.indent:match( "^%d+$" ) then
| |
| r = tonumber( frame.args.indent )
| |
| end
| |
| return r
| |
| end -- flip()
| |
|
| |
|
| |
|
| |
| JSONutil.Encoder.Array = function ( apply, adapt, alert )
| |
| -- Convert table to JSON Array
| |
| -- Parameter:
| |
| -- apply -- table, with sequence of raw elements, or
| |
| -- string, with formatted Array, or empty
| |
| -- adapt -- string, with requested type, or not
| |
| -- alert -- true, if non-numeric elements shall trigger errors
| |
| -- Returns:
| |
| -- string, with JSON Array
| |
| local r = type( apply )
| |
| if r == "string" then
| |
| r = mw.text.trim( apply )
| |
| if r == "" then
| |
| r = "[]"
| |
| elseif r:byte( 1, 1 ) ~= 0x5B or
| |
| r:byte( -1, -1 ) ~= 0x5D then
| |
| r = false
| |
| end
| |
| elseif r == "table" then
| |
| local n = 0
| |
| local strange
| |
| for k, v in pairs( apply ) do
| |
| if type( k ) == "number" then
| |
| if k > n then
| |
| n = k
| |
| end
| |
| elseif alert then
| |
| if strange then
| |
| strange = strange .. " "
| |
| else
| |
| strange = ""
| |
| end
| |
| strange = strange .. tostring( k )
| |
| end
| |
| end -- for k, v
| |
| if strange then
| |
| r = string.format( "{ \"%s\": \"%s\" }",
| |
| JSONutil.Encoder.scream,
| |
| JSONutil.Encoder.string( strange ) )
| |
| elseif n > 0 then
| |
| local sep = ""
| |
| local scope = adapt or "string"
| |
| local s
| |
| if type( JSONutil.Encoder[ scope ] ) ~= "function" then
| |
| scope = "string"
| |
| end
| |
| r = " ]"
| |
| for i = n, 1, -1 do
| |
| s = JSONutil.Encoder[ scope ]( apply[ i ] )
| |
| r = string.format( "%s%s%s", s, sep, r )
| |
| sep = ",\n "
| |
| end -- for i = n, 1, -1
| |
| r = "[ " .. r
| |
| else
| |
| r = "[]"
| |
| end
| |
| else
| |
| r = false
| |
| end
| |
| if not r then
| |
| r = string.format( "[ \"%s * %s\" ]",
| |
| JSONutil.Encoder.scream,
| |
| "Bad Array" )
| |
| end
| |
| return r
| |
| end -- JSONutil.Encoder.Array()
| |
|
| |
|
| |
|
| |
| JSONutil.Encoder.boolean = function ( apply )
| |
| -- Convert string to JSON boolean
| |
| -- Parameter:
| |
| -- apply -- string, with value
| |
| -- Returns:
| |
| -- boolean as string
| |
| local r = mw.text.trim( apply )
| |
| if r == "" or r == "null" or r == "false"
| |
| or r == "0" or r == "-" then
| |
| r = "false"
| |
| else
| |
| r = "true"
| |
| end
| |
| return r
| |
| end -- JSONutil.Encoder.boolean()
| |
|
| |
|
| |
|
| |
| JSONutil.Encoder.Component = function ( access, apply,
| |
| adapt, align, alert )
| |
| -- Create single entry for mapping object
| |
| -- Parameter:
| |
| -- access -- string, with component name
| |
| -- apply -- component value
| |
| -- adapt -- string, with value type, or not
| |
| -- align -- number, of indentation level, or not
| |
| -- alert --
| |
| -- Returns:
| |
| -- string, with JSON fragment, and comma
| |
| local v = apply
| |
| local types = adapt
| |
| local indent, liner, scope, sep, sign
| |
| if type( access ) == "string" then
| |
| sign = mw.text.trim( access )
| |
| if sign == "" then
| |
| sign = false
| |
| end
| |
| end
| |
| if type( types ) == "string" then
| |
| types = mw.text.split( mw.text.trim( types ), "%s+" )
| |
| end
| |
| if type( types ) ~= "table" then
| |
| types = { }
| |
| table.insert( types, "string" )
| |
| end
| |
| if #types == 1 then
| |
| scope = types[ 1 ]
| |
| else
| |
| for i = 1, #types do
| |
| if types[ i ] == "boolean" then
| |
| if v == "1" or v == 1 or v == true then
| |
| v = "true"
| |
| scope = "boolean"
| |
| elseif v == "0" or v == 0 or v == false then
| |
| v = "false"
| |
| scope = "boolean"
| |
| end
| |
| if scope then
| |
| types = { }
| |
| break -- for i
| |
| else
| |
| table.remove( types, i )
| |
| end
| |
| end
| |
| end -- for i
| |
| for i = 1, #types do
| |
| if types[ i ] == "number" then
| |
| if tonumber( v ) then
| |
| v = tostring( v )
| |
| scope = "number"
| |
| types = { }
| |
| break -- for i
| |
| else
| |
| table.remove( types, i )
| |
| end
| |
| end
| |
| end -- for i
| |
| end
| |
| scope = scope or "string"
| |
| if type( JSONutil.Encoder[ scope ] ) ~= "function" then
| |
| scope = "string"
| |
| elseif scope == "I18N" then
| |
| scope = "Polyglott"
| |
| end
| |
| if scope == "string" then
| |
| v = v or ""
| |
| end
| |
| if type( align ) == "number" and align > 0 then
| |
| indent = math.floor( align )
| |
| if indent == 0 then
| |
| indent = false
| |
| end
| |
| end
| |
| if scope == "object" or not sign then
| |
| liner = true
| |
| elseif scope == "string" then
| |
| local k = mw.ustring.len( sign ) + mw.ustring.len( v )
| |
| if k > 60 then
| |
| liner = true
| |
| end
| |
| end
| |
| if liner then
| |
| if indent then
| |
| sep = "\n" .. string.rep( " ", indent )
| |
| else
| |
| sep = "\n"
| |
| end
| |
| else
| |
| sep = " "
| |
| end
| |
| if indent then
| |
| indent = indent + 1
| |
| end
| |
| return string.format( " \"%s\":%s%s,\n",
| |
| sign or "???",
| |
| sep,
| |
| JSONutil.Encoder[ scope ]( v, indent ) )
| |
| end -- JSONutil.Encoder.Component()
| |
|
| |
|
| |
|
| |
| JSONutil.Encoder.Hash = function ( apply, adapt, alert )
| |
| -- Create entries for mapping object
| |
| -- Parameter:
| |
| -- apply -- table, with element value assignments
| |
| -- adapt -- table, with value types assignment, or not
| |
| -- Returns:
| |
| -- string, with JSON fragment, and comma
| |
| local r = ""
| |
| local s
| |
| for k, v in pairs( apply ) do
| |
| if type( adapt ) == "table" then
| |
| s = adapt[ k ]
| |
| end
| |
| r = r .. JSONutil.Encoder.Component( tostring( k ), v, s )
| |
| end -- for k, v
| |
| return
| |
| end -- JSONutil.Encoder.Hash()
| |
|
| |
|
| |
|
| |
| JSONutil.Encoder.I18N = function ( apply, align )
| |
| -- Convert multilingual string table to JSON
| |
| -- Parameter:
| |
| -- apply -- table, with mapping object
| |
| -- align -- number, of indentation level, or not
| |
| -- Returns:
| |
| -- string, with JSON object
| |
| local r = type( apply )
| |
| if r == "table" then
| |
| local strange
| |
| local fault = function ( a )
| |
| if strange then
| |
| strange = strange .. " *\n "
| |
| else
| |
| strange = ""
| |
| end
| |
| strange = strange .. a
| |
| end
| |
| local got, sep, indent
| |
| for k, v in pairs( apply ) do
| |
| if type( k ) == "string" then
| |
| k = mw.text.trim( k )
| |
| if type( v ) == "string" then
| |
| v = mw.text.trim( v )
| |
| if v == "" then
| |
| fault( string.format( "%s %s=",
| |
| "Empty text", k ) )
| |
| end
| |
| if not ( k:match( "%l%l%l?" ) or
| |
| k:match( "%l%l%l?-%u%u" ) or
| |
| k:match( "%l%l%l?-%u%l%l%l+" ) ) then
| |
| fault( string.format( "%s %s=",
| |
| "Strange language code",
| |
| k ) )
| |
| end
| |
| else
| |
| v = tostring( v )
| |
| fault( string.format( "%s %s=%s",
| |
| "Bad type for text",
| |
| k,
| |
| type( v ) ) )
| |
| end
| |
| got = got or { }
| |
| got[ k ] = v
| |
| else
| |
| fault( string.format( "%s %s: %s",
| |
| "Bad language code type",
| |
| type( k ),
| |
| tostring( k ) ) )
| |
| end
| |
| end -- for k, v
| |
| if not got then
| |
| fault( "No language codes" )
| |
| got = { }
| |
| end
| |
| if strange then
| |
| got[ JSONutil.Encoder.scream ] = strange
| |
| end
| |
| r = false
| |
| if type( align ) == "number" and align > 0 then
| |
| indent = math.floor( align )
| |
| else
| |
| indent = 0
| |
| end
| |
| sep = string.rep( " ", indent + 1 )
| |
| for k, v in pairs( got ) do
| |
| if r then
| |
| r = r .. ",\n"
| |
| else
| |
| r = ""
| |
| end
| |
| r = string.format( "%s %s%s: %s",
| |
| r,
| |
| sep,
| |
| JSONutil.Encoder.string( k ),
| |
| JSONutil.Encoder.string( v ) )
| |
| end -- for k, v
| |
| r = string.format( "{\n%s\n%s}", r, sep )
| |
| elseif r == "string" then
| |
| r = JSONutil.Encoder.string( apply )
| |
| else
| |
| r = string.format( "{ \"%s\": \"%s: %s\" }",
| |
| JSONutil.Encoder.scream,
| |
| "Bad Lua type",
| |
| r )
| |
| end
| |
| return r
| |
| end -- JSONutil.Encoder.I18N()
| |
|
| |
|
| |
|
| |
| JSONutil.Encoder.number = function ( apply )
| |
| -- Convert string to JSON number
| |
| -- Parameter:
| |
| -- apply -- string, with presumable number
| |
| -- Returns:
| |
| -- number, or "NaN"
| |
| local s = mw.text.trim( apply )
| |
| JSONutil.Encoder.minus = JSONutil.Encoder.minus or
| |
| mw.ustring.char( 0x2212 )
| |
| s = s:gsub( JSONutil.Encoder.minus, "-" )
| |
| return tonumber( s:lower() ) or "NaN"
| |
| end -- JSONutil.Encoder.number()
| |
|
| |
|
| |
|
| |
| JSONutil.Encoder.object = function ( apply, align )
| |
| -- Create mapping object
| |
| -- Parameter:
| |
| -- apply -- string, with components, may end with comma
| |
| -- align -- number, of indentation level, or not
| |
| -- Returns:
| |
| -- string, with JSON fragment
| |
| local story = mw.text.trim( apply )
| |
| local start = ""
| |
| if story:sub( -1 ) == "," then
| |
| story = story:sub( 1, -2 )
| |
| end
| |
| if type( align ) == "number" and align > 0 then
| |
| local indent = math.floor( align )
| |
| if indent > 0 then
| |
| start = string.rep( " ", indent )
| |
| end
| |
| end
| |
| return string.format( "%s{ %s\n%s}", start, story, start )
| |
| end -- JSONutil.Encoder.object()
| |
|
| |
|
| |
|
| |
| JSONutil.Encoder.Polyglott = function ( apply, align )
| |
| -- Convert string or multilingual string table to JSON
| |
| -- Parameter:
| |
| -- apply -- string, with string or object
| |
| -- align -- number, of indentation level, or not
| |
| -- Returns:
| |
| -- string
| |
| local r = type( apply )
| |
| if r == "string" then
| |
| r = mw.text.trim( apply )
| |
| if not r:match( "^{%s*\"" ) or
| |
| not r:match( "\"%s*}$" ) then
| |
| r = JSONutil.Encoder.string( r )
| |
| end
| |
| else
| |
| r = string.format( "{ \"%s\": \"%s: %s\" }",
| |
| JSONutil.Encoder.scream,
| |
| "Bad Lua type",
| |
| r )
| |
| end
| |
| return r
| |
| end -- JSONutil.Encoder.Polyglott()
| |
|
| |
|
| |
|
| |
| JSONutil.Encoder.string = function ( apply )
| |
| -- Convert plain string to strict JSON string
| |
| -- Parameter:
| |
| -- apply -- string, with plain string
| |
| -- Returns:
| |
| -- string, with quoted trimmed JSON string
| |
| return string.format( "\"%s\"",
| |
| mw.text.trim( apply )
| |
| :gsub( "\\", "\\\\" )
| |
| :gsub( "\"", "\\\"" )
| |
| :gsub( JSONutil.Encoder.sep, "\\n" )
| |
| :gsub( JSONutil.Encoder.stab, "\\t" ) )
| |
| end -- JSONutil.Encoder.string()
| |
|
| |
|
|
| |
|
Zeile 447: |
Zeile 32: |
| local n = 0 | | local n = 0 |
| local s = mw.text.trim( apply ) | | local s = mw.text.trim( apply ) |
| local i, j, last, r, scan, sep0, sep1, start, stub, suffix | | local sep = string.char( 10 ) |
| | local i, j, last, r, scan, sep0, sep1, stab, start, stub, suffix |
| local framework = function ( a ) | | local framework = function ( a ) |
| -- syntax analysis outside strings | | -- syntax analysis outside strings |
Zeile 516: |
Zeile 102: |
| end -- free() | | end -- free() |
| if s:sub( 1, 1 ) == '{' then | | if s:sub( 1, 1 ) == '{' then |
| s = s:gsub( string.char( 13, 10 ), JSONutil.Encoder.sep ) | | stab = string.char( 9 ) |
| :gsub( string.char( 13 ), JSONutil.Encoder.sep ) | | s = s:gsub( string.char( 13, 10 ), sep ) |
| stub = s:gsub( JSONutil.Encoder.sep, "" ) | | :gsub( string.char( 13 ), sep ) |
| :gsub( JSONutil.Encoder.stab, "" )
| | stub = s:gsub( sep, "" ):gsub( stab, "" ) |
| scan = string.char( 0x5B, 0x01, 0x2D, 0x1F, 0x5D ) -- [ \-\ ] | | scan = string.char( 0x5B, 0x01, 0x2D, 0x1F, 0x5D ) -- [ \-\ ] |
| j = stub:find( scan ) | | j = stub:find( scan ) |
Zeile 573: |
Zeile 159: |
| if j > i then | | if j > i then |
| stub = s:sub( i, j - 1 ) | | stub = s:sub( i, j - 1 ) |
| :gsub( JSONutil.Encoder.sep, | | :gsub( sep, "\\n" ) |
| "\\n" )
| | :gsub( stab, "\\t" ) |
| :gsub( JSONutil.Encoder.stab, | |
| "\\t" )
| |
| j = i + stub:len() | | j = i + stub:len() |
| s = string.format( "%s%s%s", | | s = string.format( "%s%s%s", |
Zeile 660: |
Zeile 244: |
| end | | end |
| if r and s then | | if r and s then |
| s = s:gsub( JSONutil.Encoder.sep, " " ) | | s = mw.text.encode( s:gsub( sep, " " ) ):gsub( "|", "|" ) |
| s = mw.text.encode( s ):gsub( "|", "|" )
| |
| end | | end |
| return r, s | | return r, s |
Zeile 779: |
Zeile 362: |
| -- Retrieve versioning and check for compliance | | -- Retrieve versioning and check for compliance |
| -- Precondition: | | -- Precondition: |
| -- atleast -- string, with required version | | -- atleast -- string, with required version or "wikidata" or "~" |
| -- or "wikidata" or "~" or "@" or false
| | -- or false |
| -- Postcondition: | | -- Postcondition: |
| -- Returns string -- with queried version/item, also if problem | | -- Returns string -- with queried version, also if problem |
| -- false -- if appropriate | | -- false -- if appropriate |
| -- 2020-08-17 | | local last = ( atleast == "~" ) |
| local since = atleast | | local since = atleast |
| local last = ( since == "~" )
| |
| local linked = ( since == "@" )
| |
| local link = ( since == "item" )
| |
| local r | | local r |
| if last or link or linked or since == "wikidata" then | | if last or since == "wikidata" then |
| local item = Failsafe.item | | local item = Failsafe.item |
| since = false | | since = false |
| if type( item ) == "number" and item > 0 then | | if type( item ) == "number" and item > 0 then |
| local suited = string.format( "Q%d", item ) | | local entity = mw.wikibase.getEntity( string.format( "Q%d", |
| if link then | | item ) ) |
| r = suited
| | if type( entity ) == "table" then |
| else
| | local vsn = entity:formatPropertyValues( "P348" ) |
| local entity = mw.wikibase.getEntity( suited )
| | if type( vsn ) == "table" and |
| if type( entity ) == "table" then
| | type( vsn.value ) == "string" and |
| local seek = Failsafe.serialProperty or "P348"
| | vsn.value ~= "" then |
| local vsn = entity:formatPropertyValues( seek )
| | if last and vsn.value == Failsafe.serial then |
| if type( vsn ) == "table" and
| | r = false |
| type( vsn.value ) == "string" and
| | else |
| vsn.value ~= "" then
| | r = vsn.value |
| if last and vsn.value == Failsafe.serial then
| |
| r = false
| |
| elseif linked then | |
| if mw.title.getCurrentTitle().prefixedText
| |
| == mw.wikibase.getSitelink( suited ) then
| |
| r = false
| |
| else
| |
| r = suited
| |
| end
| |
| else | |
| r = vsn.value
| |
| end
| |
| end | | end |
| end | | end |
Zeile 853: |
Zeile 421: |
| end | | end |
| return Failsafe.failsafe( since ) or "" | | return Failsafe.failsafe( since ) or "" |
| end -- p.failsafe | | end -- p.failsafe() |
| | |
| | |
| p.encodeArray = function ( frame )
| |
| return JSONutil.Encoder.Array( frame:getParent().args,
| |
| frame.args.type,
| |
| frame.args.error == "1" )
| |
| end -- p.encodeArray
| |
| | |
| | |
| p.encodeComponent = function ( frame )
| |
| return JSONutil.Encoder.Component( frame.args.sign,
| |
| frame.args.value,
| |
| frame.args.type,
| |
| flip( frame ),
| |
| frame.args.error == "1" )
| |
| end -- p.encodeComponent
| |
| | |
| | |
| p.encodeHash = function ( frame )
| |
| return JSONutil.Encoder.Hash( frame:getParent().args,
| |
| frame.args )
| |
| end -- p.encodeHash
| |
| | |
| | |
| p.encodeI18N = function ( frame )
| |
| return JSONutil.Encoder.I18N( frame:getParent().args,
| |
| flip( frame ) )
| |
| end -- p.encodeI18N
| |
| | |
| | |
| p.encodeObject = function ( frame )
| |
| return JSONutil.Encoder.object( flat( frame.args[ 1 ] ),
| |
| flip( frame ) )
| |
| end -- p.encodeObject
| |
| | |
| | |
| p.encodePolyglott = function ( frame )
| |
| return JSONutil.Encoder.Polyglott( flat( frame.args[ 1 ] ),
| |
| flip( frame ) )
| |
| end -- p.encodePolyglott
| |
| | |
|
| |
|
| p.JSONutil = function () | | p.JSONutil = function () |