Fala XTibianos!
Vim disponibilizar e ensinar como usar a livraria xml em lua, desenvolvida por mim.
Ela foi desenvolvida para substituir a livraria padrão disponibilizada por lua para trabalhar com dados xml.
Essa livraria não requer a dll-xml necessária para a livraria padrão lua.
Aqui tem o tutorial criado por mim para demonstrar as funções e suas implementações da livraria luaxml.
Vamos lá.
Primeiro, a livraria:
--[=[ ################################################################################################################# ## LibXml desenvolvida por Marcryzius ## ## Terminado na data: 23 de Julho de 2016 ## ## Ultima modificação: 5 de outubro de 2017 ## ## Livre para quem queira usa-la ou modifica-la (caso modifique, favor postar suas modificações para ajudar outros). ## ################################################################################################################# ]=] xml = {} -- carrega o arquivo e retorna um objeto para os metametodos ou uma string para outros fins. function xml:load(dir) local open,read_open = io.open(dir,'r'),"<?xml version=\"1.0\"?>\n<!-- file \"",dir, "\", criado por LuaXML system desenvolvido por Marcryzius -->\n\n" if(open)then read_open = open:read(-1) open:close() end return setmetatable({},{__index = xml, __tostring = function() return read_open end}) end -- Converte strings formatadas em xml para tabelas com parametros validos em lua. retorna uma tabela. function xml:eval(tag,head) local tab,index,tag = {},0,type(tag) == 'string' and tag or tostring(self) if(string.match(tag,'<.-/>'))then --<tag name="carrot" quant="914"/> for head,corpser in string.gmatch(tag,'<(%a+) (.-)/>') do if not(tab[index+1])then tab[index+1] = { head = head, _M = 'tag' } end for k,v in string.gmatch(corpser,'(%a+).-"(.-)"') do tab[index+1][k] = v end index = index+1 end elseif(string.match(tag,'<.->'))then if(string.match(tag,'<(%a+)>'))then --<bloco> </bloco> tab.head = string.match(tag,'<(%a+)>') tab._M = 'bloco' for ke,v in pairs(xml:eval(head)) do tab[ke] = v end elseif(string.match(tag,'<(%a+).->'))then --<string id="author"> Corpo com string </string> tab.head = string.match(tag,'<(%a+).->') tab._M = 'tagBloco' for ke,v in string.gmatch(string.match(tag,'<%a+ (.-)>'),'(%a+).-"(.-)"') do tab[ke] = v end for ke,v in pairs(xml:eval(head)) do tab[ke] = v end end end return tab end -- converte tabelas e parametros com argumentos pre-determinados para xml, em uma string formatada em xml. retorna uma string. function xml:str() local str = '' if(self._M == 'tag')then --<tag name="carrot" quant="914"/> str = '\n<'..(self.tag or '')..' ' for k,v in pairs(self) do if not(k == '_M') and not(k == 'head') and not(k == 'tag') and not(type(v) == 'table') and not(tonumber(k)) then str = str..k..' = "'..v..'" ' end end str = str..'/>' elseif(self._M == 'tagBloco')then local taghead,corpo = '','' --<string id="author"> Corpo com string </string> for k,v in pairs(self) do if not(type(v) == 'table')then if not(k == '_M') and not(k == 'head') and not(tonumber(k)) then taghead = taghead..' '..k..'="'..v..'"' elseif(k == '_string')then corpo = corpo..' '..v end elseif(type(v) == 'table')then corpo = corpo..xml.str(v) end end str = '\n<'..(self.head or 'tag')..taghead..'>'..corpo..'\n</'..(self.head or 'tag')..'>' elseif(self._M == 'bloco')then --<bloco> </bloco> local corpo = '' str = '<'..self.head..'>' for k,v in pairs(self) do if(tonumber(k) and type(v) == 'table')then corpo = corpo..xml.str(v) end end str = str..corpo..'\n</'..self.head..'>' end return str end -- Busca um bloco xml atraves dos argumentos fornecidos. retorna um objeto para os metametodos ou uma string para outros fins. function xml:find(headTag,param,value) local tab = {} local tag,head,final = nil,nil,headTag:match("%%") and headTag or headTag:match("(%a+)") if not(headTag) or type(headTag) ~= 'string' then return false elseif not(string.find(tostring(self),'<?xml version="1.0".->'))then --<?xml version="1.0" encoding="UTF-8"?> return false,print('Versao do arquivo xml nao aceita ou desconhecida - use versao 1.0') elseif not(param and value)then tag,head = string.match(tostring(self),'(<'..headTag..'>)(.-)</'..final..'>') if not(tag)then tag,head = string.match(tostring(self),'(<'..headTag..'.->)(.-)</'..final..'>') end elseif tostring(param) then for tg,hd in string.gmatch(tostring(self),'(<'..headTag..'.->)(.-)</'..final..'>') do tg = string.find(tg,'/') and string.sub(tg,string.find(tg,'<',2) or 1,-1) or tg if(string.find(tg,' '..param..'="'..value..'"'))then tag,head = tg,hd end end end if(tag and head)then tab = xml:eval(tag,head) end return setmetatable(tab,{__index = xml, __tostring = xml.str}) end -- Cria um novo objeto xml com o nome da taghead inserido. function xml:new(arg) return setmetatable({head = arg or 'xmltag', _M = 'bloco'},{__index = xml, __tostring = xml.str}) end -- Essa função não é equivalente a da libluaxml que modificar o nome da taghead. -- Essa função cria uma tag do tipo >> <tagExemplo valor="1" novo="new"/> function xml:tag(arg,param) -- arg, seria o nome da tag (<tagExemplo .../>) -- param, seria uma tabela contendo todos os valores que serão postos na tag ({valor=1,novo="new"}). if(type(arg) ~= 'string')then return print(type(param) == 'string' and param or type(param)) end local tab = {tag=arg,_M='tag'} if(type(param) == 'table')then for k,v in pairs(param) do tab[k] = v end end table.insert(self,tab) end -- Essa função não é equivalente a da libluaxml. -- Essa função cria uma tag do tipo >> <tagExemplo pode="ter" valores="aqui" opcional=":)">valores inseridos aqui tambem (obs. com uso de metametodos)</tagExemplo> function xml:append(arg,param) -- arg, seria o nome da tag (<tagExemplo .../>) -- param, seria uma tabela contendo todos os valores que serão postos na tag ({valor=1,novo="new"}). local tab = {head=arg,_M='tagBloco'} if(type(param) == 'table')then for k,v in pairs(param) do tab[k] = v end end if(type(param) == 'string')then table.insert(tab,param) end table.insert(self,tab) return setmetatable(tab,{__index = xml, __tostring = xml.str}) end -- salva os dados em um arquivo xml -- Se, já existir o arquivo, ele será substituido. function xml:save(dir) local dir = type(dir) == 'string' and #dir > 4 and dir or 'newFileByXmlGenerator.xml' if not(string.find(dir,'.xml'))then dir = dir..'.xml' end local file = io.open(dir,"w") file:write("<?xml version=\"1.0\"?>\n<!-- file \"",dir, "\", criado por LuaXML; livraria desenvolvida por Marcryzius -->\n\n") file:write(self:str()) file:close() end
Vou ensinar como usar ela.
Vou usar como exemplo uma parte do arquivo vocations.xml:
<vocation id="1" name="Sorcerer" description="a Sorcerer" needpremium="0" gaincap="10" gainhp="5" gainmana="30" gainhpticks="1" gainhpamount="0.8" gainmanaticks="1" gainmanaamount="6.7" manamultiplier="1.1" attackspeed="2000" soulmax="100" gainsoulticks="600" fromvoc="1"> <formula meleeDamage="1.0" distDamage="1.0" wandDamage="1.0" magDamage="1.1" magHealingDamage="1.0" defense="1.0" magDefense="1.2" armor="1.0"/> <skill fist="1.5" club="2.0" sword="2.0" axe="2.0" distance="2.0" shielding="1.5" fishing="1.1" experience="1.0"/> </vocation> <vocation id="2" name="Druid" description="a Druid" needpremium="0" gaincap="10" gainhp="5" gainmana="30" gainhpticks="1" gainhpamount="0.8" gainmanaticks="1" gainmanaamount="6.7" manamultiplier="1.1" attackspeed="2000" soulmax="100" gainsoulticks="600" fromvoc="2"> <formula meleeDamage="1.0" distDamage="1.0" wandDamage="1.0" magDamage="1.1" magHealingDamage="1.1" defense="1.0" magDefense="1.1" armor="1.0"/>
Primeiro, carregar o arquivo.
Ele vai retornar um objeto para ser usado pelos metametodos ou uma string para outros fins como "print".
Citarlocal voc = xml:load('vocations.xml')
Buscar a tag desejada.
Ele vai retornar uma tabela contendo todos os parametros que foram lidos no arquivo.
O primeiro argumento da função, é o nome da tag requerida. exemplo: <tagNome ....>, tagNome seria o primeiro argumento da função.
O segundo elemento é usado para diferenciar um bloco de outro como no caso do arquivo vocations que contem varias vocations, mas apenas uma é a requerida.
O terceiro elemento é o que diferencia um bloco de outro similar. seguindo o exemplo do arquivo vocations, existe varias vocações, mas apenas uma delas tem o id de numero 2.
Citarlocal find = voc:find('vocation','id',2)
Imprime o que foi lido no arquivo.
Citarprint(find)
a saida seria essa, nesse caso.
Citar<vocation description="a Druid" gaincap="10" needpremium="0" gainhpticks="1" gainmanaamount="6.7" gainmanaticks="1" soulmax="100" id="2" attackspeed="2000" gainsoulticks="600" name="Druid" fromvoc="2" manamultiplier="1.1" gainhpamount="0.8" gainmana="30" gainhp="5"> <magHealingDamage = "1.1" defense = "1.0" magDefense = "1.1" wandDamage = "1.0" armor = "1.0" meleeDamage = "1.0" magDamage = "1.1" distDamage = "1.0" /> <fist = "1.5" axe = "1.8" fishing = "1.1" sword = "1.8" club = "1.8" experience = "1.0" shielding = "1.5" distance = "1.8" /> </vocation>
Mas, ela tambem é uma tabela, por isso, voce pode pegar os elementos dentro dela dessa forma.
Citarfind.description > "a Druid"
find.id > 2
find.needpremium > 0
...
As tags existentes dentro do bloco "vocation", tambem é uma tabela com indice numerico; portanto é valido pegar os dados dela dessa forma.
Citarfind[1].defense > 1
find[1].magDamage > 1.1
find[2].fist > 1.5
find[2].axe > 1.8
...
Cada tag que exista dentro do bloco, vem como uma tabela com indicie numerico.
Criando objetos para xml:
O argumento inserido , seria o nome do bloco. Ex -> <novo> </novo>
Citarlocal novo = xml:new('novo')
Adiciona tag's dentro do corpo do bloco.
Citarnovo:tag('print',{dir=1,move="sul"})
novo:tag('nova',{dir=2,move="norte",sem="valido"})
Pode ser adicionado quantas tags quiser.
Cria uma tagbloco ou bloco dentro do bloco principal. Ex -> <tagBloco inserido="primer" ordem="2" face="not"> </tagBloco>
Essa função retorna um objeto para ser usado pelos metametodos ou uma string para outros fins como "print".
Citarlocal tagBloco = find:append('tagBloco',{inserido='primer',ordem=2,face='not'})
Dessa forma que foi declarada sem uma tabela contendo os parametros da tag, ela é criada como um bloco. Ex -> <newBloco> </newBloco>
Citarlocal newBloco = find:append('newBloco')
Adiciona tag's ao corpo do novo bloco/tagbloco criado pela função append.
CitartagBloco:tag('inside',{system='F_ord',mac="TTvalido"})
tagBloco:tag('newInside',{system='F_ord',mac="TTvalido",base="TT_down"})
newBloco:tag('newInsideBloco',{system='F_ord',mac="TTvalido",base="TT_down"})
E por fim, salva os dados em um arquivo.
Nessa função, existe o argumento unico que é o destino do arquivo e seu nome, que nesse caso foi ocultado.
Citarfind:save() -- Não declarei diretorio e nem o nome do arquivo, logo ele será criado onde estiver o script.
Esse é os dados dentro do arquivo gerador pelo script.
Citar<?xml version="1.0"?> <!-- file "newFileByXmlGenerator.xml", criado por LuaXML system desenvolvido por Marcryzius --> <novo> <print dir="1"move="sul"> <nova move="norte"dir="2"sem="valido"> <tagBloco ordem="2" inserido="primer" face="not"> <newInside mac = "TTvalido" system = "F_ord" base = "TT_down" /> <inside mac = "TTvalido" system = "F_ord" /> </tagBloco> <newBloco> <newInsideBloco mac = "TTvalido" system = "F_ord" base = "TT_down" /> </newBloco> </novo>
Devido as novas atualizações feitas a lib, agora podeis usar caracteres especial de busca e inserção de maiores informações para busca.
Vou usar como exemplo essa parte do arquivo items.xml e movements.xml para explicar essas mudanças.
items
<item id="8899"/>
<item id="8900" article="a" name="spellbook of enlightenment">
<attribute key="description" value="esse e o primeiro livro de um mago um belo livro que da uma certa protecao a algums ataques."/>
<attribute key="weight" value="4500"/>
<attribute key="skillShield" value="-1"/>
<attribute key="magiclevelpoints" value="1"/>
<attribute key="defense" value="18"/>
<attribute key="weaponType" value="shield"/>
</item>
movements
<movevent type="DeEquip" itemid="8190" slot="shield" event="script" value="spellbooks.lua"/>
<movevent type="Equip" itemid="8900" slot="shield" level="30" event="script" value="spellbooks.lua">
<vocation id="1"/>
<vocation id="5"/>
<vocation id="2"/>
<vocation id="6"/>
<vocation id="9"/>
<vocation id="10"/>
</movevent>
<movevent type="DeEquip" itemid="8900" slot="shield" event="script" value="spellbooks.lua"/>
Na versão anterior da lib ela não poderia buscar informações no arquivo items.xml devido a um erro na ora de selecionar o bloco buscado.
O problema residia em que: ao encontrar uma tag ela busca o seu fechamento, sendo que, no exemplo da parte do arquivo items que postei acima, ela pegava todo esse bloco, dessa forma anulando o verdadeiro id buscado.
Citar<item id="8899"/>
<item id="8900" article="a" name="spellbook of enlightenment">
<attribute key="description" value="esse e o primeiro livro de um mago um belo livro que da uma certa protecao a algums ataques."/>
<attribute key="weight" value="4500"/>
<attribute key="skillShield" value="-1"/>
<attribute key="magiclevelpoints" value="1"/>
<attribute key="defense" value="18"/>
<attribute key="weaponType" value="shield"/>
</item>
Isso foi resolvido. agora ela filtra o bloco encontrado antes de fazer a analise de que seja o dado requisitado.
Outro problema era quando se desejava buscar uma informação mais precisa, como no caso do arquivo movements.xml que existe duas tags com os mesmo ids equip e deequip; se eu colocasse o id de qualquer item ele me retornaria a primeira sentença encontrada que na maioria das vezes não era a que eu queria. isso também foi corrigido; agora podeis usar mais informações para melhorar a precisão da busca. Veja esse exemplo:
Citar
local function getBookRequirementLevelByVoc(id,voc) --[[( Marcryzius )]]--
local file = xml:load('data/movements/movements.xml','r')
local lerfile = file:find("movevent type=\"Equip\"","itemid",id)for _,tab in pairs(lerfile) do
if(type(tab) == "table")then
if(tonumber(tab.id) == voc)then
return true,tonumber(lerfile.level)
end
end
end
return false
endprint(8900,5) >> true,30
Obs. Os codigos estão codificados em utf8, converta para ansi antes de usa-los.
É isso ae pessoal, espero que gostem; qualquer erro ou sugestão serão bem vindas. obrigado.