Модуль:XML

Материал из Викиэнциклопедия Московской областей

Пример

{{#invoke:XML|parseTest|1=<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
		<rss version="2.0" xmlns:yweather="http://xml.weather.yahoo.com/ns/rss/1.0" xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#">
			<channel>
		
<title>Yahoo! Weather - Klin, RS</title>
<link>http://us.rd.yahoo.com/dailynews/rss/weather/Klin__RS/*http://weather.yahoo.com/forecast/RSXX1476_c.html</link>
<description>Yahoo! Weather for Klin, RS</description>
<language>en-us</language>
<lastBuildDate>Mon, 03 Nov 2014 1:01 pm MSK</lastBuildDate>
<ttl>60</ttl>
<yweather:location city="Klin" region=""   country="RS"/>
<yweather:units temperature="C" distance="km" pressure="mb" speed="km/h"/>
<yweather:wind chill="-3"   direction="160"   speed="6.44" />
<yweather:atmosphere humidity="91"  visibility="9.99"  pressure="1018"  rising="0" />
<yweather:astronomy sunrise="8:45 am"   sunset="5:47 pm"/>
<image>
<title>Yahoo! Weather</title>
<width>142</width>
<height>18</height>
<link>http://weather.yahoo.com</link>
<url>http://l.yimg.com/a/i/brand/purplelogo//uh/us/news-wea.gif</url>
</image>
<item>
<title>Conditions for Klin, RS at 1:01 pm MSK</title>
<geo:lat>56.35</geo:lat>
<geo:long>36.75</geo:long>
<link>http://us.rd.yahoo.com/dailynews/rss/weather/Klin__RS/*http://weather.yahoo.com/forecast/RSXX1476_c.html</link>
<pubDate>Mon, 03 Nov 2014 1:01 pm MSK</pubDate>
<yweather:condition  text="Cloudy"  code="26"  temp="-1"  date="Mon, 03 Nov 2014 1:01 pm MSK" />
<description><![CDATA[
<img src="http://l.yimg.com/a/i/us/we/52/26.gif"/><br />
<b>Current Conditions:</b><br />
Cloudy, -1 C<BR />
<BR /><b>Forecast:</b><BR />
Mon - Showers. High: 2 Low: -1<br />
Tue - Partly Cloudy. High: 6 Low: 1<br />
Wed - Sunny. High: 6 Low: 1<br />
Thu - Partly Cloudy. High: 6 Low: 2<br />
Fri - Rain. High: 7 Low: 4<br />
<br />
<a href="http://us.rd.yahoo.com/dailynews/rss/weather/Klin__RS/*http://weather.yahoo.com/forecast/RSXX1476_c.html">Full Forecast at Yahoo! Weather</a><BR/><BR/>
(provided by <a href="http://www.weather.com" >The Weather Channel</a>)<br/>
]]></description>
<yweather:forecast day="Mon" date="3 Nov 2014" low="-1" high="2" text="Showers" code="11" />
<yweather:forecast day="Tue" date="4 Nov 2014" low="1" high="6" text="Partly Cloudy" code="30" />
<yweather:forecast day="Wed" date="5 Nov 2014" low="1" high="6" text="Sunny" code="32" />
<yweather:forecast day="Thu" date="6 Nov 2014" low="2" high="6" text="Partly Cloudy" code="30" />
<yweather:forecast day="Fri" date="7 Nov 2014" low="4" high="7" text="Rain" code="12" />
<guid isPermaLink="false">RSXX1476_2014_11_07_7_00_MSK</guid>
</item>
</channel>
</rss>

<!-- api5.weather.bf1.yahoo.com Mon Nov  3 03:37:09 PST 2014 -->
}}

table {
    1 = <?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
		
    rss = table {
        geo = http://www.w3.org/2003/01/geo/wgs84_pos#
        yweather = http://xml.weather.yahoo.com/ns/rss/1.0
        version = 2.0
        channel = table {
            lastBuildDate = Mon, 03 Nov 2014 1:01 pm MSK
            description = Yahoo! Weather for Klin, RS
            link = http://us.rd.yahoo.com/dailynews/rss/weather/Klin__RS/*http://weather.yahoo.com/forecast/RSXX1476_c.html
            yweather:astronomy = table {
                sunrise = 8:45 am
                sunset = 5:47 pm
            }
            yweather:location = table {
                region = 
                city = Klin
                country = RS
            }
            item = table {
                description = table {
                    a = table {
                        1 = table {
                            href = http://us.rd.yahoo.com/dailynews/rss/weather/Klin__RS/*http://weather.yahoo.com/forecast/RSXX1476_c.html
                            value = Full Forecast at Yahoo! Weather
                        }
                        2 = table {
                            href = http://www.weather.com
                            value = The Weather Channel
                        }
                    }
                    img = table {
                        src = http://l.yimg.com/a/i/us/we/52/26.gif
                    }
                    description = table {
                        1 = <![CDATA[

                        2 = 
Cloudy, -1 C

                        3 = 
Mon - Showers. High: 2 Low: -1
Tue - Partly Cloudy. High: 6 Low: 1
Wed - Sunny. High: 6 Low: 1
Thu - Partly Cloudy. High: 6 Low: 2
Fri - Rain. High: 7 Low: 4


                        4 = 
(provided by 
                        5 = )
]]>
                    }
                    b = table {
                        1 = Current Conditions:
                        2 = Forecast:
                    }
                }
                link = http://us.rd.yahoo.com/dailynews/rss/weather/Klin__RS/*http://weather.yahoo.com/forecast/RSXX1476_c.html
                pubDate = Mon, 03 Nov 2014 1:01 pm MSK
                yweather:forecast = table {
                    1 = table {
                        low = -1
                        day = Mon
                        code = 11
                        date = 3 Nov 2014
                        high = 2
                        text = Showers
                    }
                    2 = table {
                        low = 1
                        day = Tue
                        code = 30
                        date = 4 Nov 2014
                        high = 6
                        text = Partly Cloudy
                    }
                    3 = table {
                        low = 1
                        day = Wed
                        code = 32
                        date = 5 Nov 2014
                        high = 6
                        text = Sunny
                    }
                    4 = table {
                        low = 2
                        day = Thu
                        code = 30
                        date = 6 Nov 2014
                        high = 6
                        text = Partly Cloudy
                    }
                    5 = table {
                        low = 4
                        day = Fri
                        code = 12
                        date = 7 Nov 2014
                        high = 7
                        text = Rain
                    }
                }
                title = Conditions for Klin, RS at 1:01 pm MSK
                yweather:condition = table {
                    date = Mon, 03 Nov 2014 1:01 pm MSK
                    text = Cloudy
                    temp = -1
                    code = 26
                }
                geo:lat = 56.35
                geo:long = 36.75
                guid = table {
                    isPermaLink = false
                    value = RSXX1476_2014_11_07_7_00_MSK
                }
            }
            image = table {
                title = Yahoo! Weather
                link = http://weather.yahoo.com
                height = 18
                url = http://l.yimg.com/a/i/brand/purplelogo//uh/us/news-wea.gif
                width = 142
            }
            title = Yahoo! Weather - Klin, RS
            yweather:atmosphere = table {
                humidity = 91
                pressure = 1018
                visibility = 9.99
                rising = 0
            }
            yweather:wind = table {
                speed = 6.44
                direction = 160
                chill = -3
            }
            yweather:units = table {
                speed = km/h
                temperature = C
                distance = km
                pressure = mb
            }
            language = en-us
            ttl = 60
        }
    }
}

-- Роберту Иерусалимски
-- http://lua-users.org/wiki/LuaXml
local function parseargs(s)
	local arg = {}
	string.gsub(s, "([%-%w]+)=([\"'])(.-)%2", function (w, _, a)
		arg[w] = a
	end)
	return arg
end

local function collect (s)
	local stack = {}
	local top = {}
	table.insert(stack, top)
	local ni,c,label,xarg, empty
	local i, j = 1, 1
	while true do
		ni,j,c,label,xarg, empty = mw.ustring.find(s, "<(%/?)([%w:]+)(.-)(%/?)>", i)
		if not ni then break end
		local text = string.sub(s, i, ni-1)
		if not mw.ustring.find(text, "^%s*$") then
			table.insert(top, text)
		end
		if empty == "/" then	-- empty element tag
			table.insert(top, {label=label, xarg=parseargs(xarg), empty=1})
		elseif c == "" then	 -- start tag
			top = {label=label, xarg=parseargs(xarg)}
			table.insert(stack, top)	 -- new level
		else	-- end tag
			local toclose = table.remove(stack)	-- remove top
			top = stack[#stack]
			if #stack < 1 then
				error("nothing to close with "..label)
			end
			if toclose.label ~= label then
				error("trying to close "..toclose.label.." with "..label)
			end
			table.insert(top, toclose)
		end
		i = j+1
	end
	local text = mw.ustring.sub(s, i)
	if not mw.ustring.find(text, "^%s*$") then
		table.insert(stack[#stack], text)
	end
	if #stack > 1 then
		error("unclosed "..stack[#stack].label)
	end
	return stack[1]
end

-- Свёртывание таблицы в более читаемый формат:
local function compactify (tbl)
	-- Завершение рекурсии:
	if type (tbl) ~= 'table' then
		return nil, tbl
	end
	-- В поле label имя тега:
	local index = tbl.label or 1
	tbl.label = nil
	local ret = {}	
	-- Атрибуты тегов:
	local attributes = false
	if tbl.xarg then
		for attr, val in pairs (tbl.xarg) do
			attributes = true
			ret [attr] = val
		end
	end
	tbl.xarg = nil
	-- Единственный элемент -- рекурсия скоро завершится:
	local label, value
	if #tbl == 1 then
		label, value = compactify (tbl [1])
		if label then
			ret [label] = value
		else
			if type (value) == 'table' then
				for k, v in pairs (value) do
					ret [k] = v
				end
			else
				if attributes then
					ret.value = value
				else
					ret = value
				end
			end
		end
	else
		-- Возможны и единичные и множественные теги:
		local multiple = {}
		for item, val in ipairs (tbl) do
			label, value = compactify (val)
			label = label or index
			-- Этот тег оказался множественным:
			if ret [label] and not multiple [label] then
				ret [label] = {ret [label]}
				multiple [label] = true
			end
			if multiple [label] then
				ret [label] [#ret [label] + 1] = value
			else
				ret [label] = value
			end
		end
	end
	return index, ret
end

-- Удаление <br />
local function stripBR (str)
	return mw.ustring.gsub (mw.ustring.gsub (str, '<br%s*/?>', ''), '<BR%s*/?>', '');
end

-- Интерфейс к {{#invoke:}}:
local p = {}

function p.parse (xml)
     local _, ret = compactify (collect (stripBR (xml)))
     return ret
end

function p.parseTest (frame)
     local xml = frame.args [1]	
     return require ("Модуль:Test").serialise (p.parse (xml))
end

return p