Ir para conteúdo

Nova livraria lua para xml


Poccnn

Posts Recomendados

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.

Tutorial lua lib xml padrão

 

Vamos lá.
Primeiro, a livraria: 

Spoiler

--[=[
#################################################################################################################
##																			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: 

   

Spoiler

<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". 

Citar

local 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. 

Citar

local find = voc:find('vocation','id',2) 

 

Imprime o que foi lido no arquivo. 

Citar

print(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. 

Citar

find.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. 

Citar

find[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> 

Citar

local novo = xml:new('novo') 

 

Adiciona tag's dentro do corpo do bloco. 

Citar

novo: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". 

Citar

local 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> 

Citar

local newBloco = find:append('newBloco') 

 

Adiciona tag's ao corpo do novo bloco/tagbloco criado pela função append. 

Citar

tagBloco: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. 

Citar

find: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

Spoiler


    <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

Spoiler


    <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
end

print(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.
 

Editado por Poccnn
Inserção de informações/atualização
Link para o comentário
Compartilhar em outros sites

  • 2 weeks later...

Muito bom!!

 

 

@suporte no lugar errado

Teve uma coisa que me intrigou.. você mudou o comportamento do __tostring

local t = {}setmetatable (t, {__tostring = function () return "test" end})print (t)

Deveria ser a mesma coisa, mas não printa.

Link para o comentário
Compartilhar em outros sites

Vou tentar te explicar o que esta acontecendo.

 

Por padrao a sentença __index é a propria tabela, mesmo que voce nao declare ela, ela existe. 

 

__index, seria - como o proprio nome sugere - o inicio por onde o sistema vai iniciar seus metodos de busca de processos para serem executados por lua; por isso que ao ser chamado a tabela, ele retornar a tabela propriamente (__index por padrao).

 

Primeiro ele executa o que esta apontado pelo index - no seu caso, a tabela - e caso nao possa ser executado, ele vai em busca de outros metodos (caso exista).

 

No meu caso - no sistema por mim criado para xml, ou seja, esse que expus - ele tem como __index a tabela xml com metametodos. Quando o sistema nao encontra uma funcao compativel com a feita pela chamada, ele vai em busca de outra chave - no caso, __tostring - para ser executada; por isso que ele retornar uma tabela ou string dependendo do tipo de chamada.

 

Se voce quiser - dependendo do metodo que voce use - usar a string - se for declarada no objeto - use tostring() para que ele faça a chamada na chave __tostring.

 

Ficou meio complicado mas, espero que tenha entendido.

Link para o comentário
Compartilhar em outros sites

Eu entendo como funciona:

 

local t = {}local b = {2}setmetatable (t, {__index = b})print (t[1])

 

O problema não foi esse. O problema provavelmente foi a distro que usei, que altera o funcionamento padrão do print(), tirando uma chamada do tostring() que ela possui.

Utilizei tfs 0.4 pra testar isso. Caso eu fizesse assim:

 

local t = {}setmetatable (t, {__tostring = function () return "LOL" end})print (tostring(t))

ai sim, funcionava.

 

Pois então, abri o https://repl.it/

E rodei o código sem o tostring e claro, LOL foi printado como esperado.

 

Desculpa pelo offtopic, sua library foi realmente boa. Caso eu sinta necessidade do uso do XML, certamente a utilizarei.

 

Abraço.

 

 

#edit

achei onde o print foi mudado.

compat.lua

print = std.cout

 

Editado por Marce Loko
Link para o comentário
Compartilhar em outros sites

  • 3 weeks later...

Mais uma atualização. Encontrei um erro crítico no sistema de criação do código xml.

Se alguém encontrar qualquer erro ou anomalia no funcionamento da lib, me comunique para que possa resolve-lo.

Link para o comentário
Compartilhar em outros sites

  • 4 months later...
Em 08/09/2016 at 11:07, Poccnn disse:

Mais uma atualização. Encontrei um erro crítico no sistema de criação do código xml.

Se alguém encontrar qualquer erro ou anomalia no funcionamento da lib, me comunique para que possa resolve-lo.

boa noite man tou querendo add isso no meu serve mas n tenho certeza dos locais de onde eu crio o arquivo (o tutorial fala da pasta do executavel)

mas vi que são scripts diferentes (não sei se foi apenas atualizado) então gostaria de saber o local certo

Link para o comentário
Compartilhar em outros sites

Pessoal fiz uma nova atualização na livraria.

Agora além dela aceitar caracteres de busca, ela também aceita novos elementos internos para melhorar a busca.

Confira na explicação que dei no tópico.

Link para o comentário
Compartilhar em outros sites

  • 5 months later...

queria ver o codigo, mas por algum motivo, aki ele aparece como um codigo uma linha gigante, tipo akeles de frameworks, e n consigo copiar ele de forma apropriada (com indentação e as coisas nas devidas linha separadas), o que tbm impossibilita de rodar, pq como ta numa linha so, depois do -- tudo é considerado comentario

tentei abrir tanto no firefox, como no chrome, pra ver se em algum abria certo 

 

Link para o comentário
Compartilhar em outros sites

23 horas atrás, Vincent Solus disse:

queria ver o codigo, mas por algum motivo, aki ele aparece como um codigo uma linha gigante, tipo akeles de frameworks, e n consigo copiar ele de forma apropriada (com indentação e as coisas nas devidas linha separadas), o que tbm impossibilita de rodar, pq como ta numa linha so, depois do -- tudo é considerado comentario

tentei abrir tanto no firefox, como no chrome, pra ver se em algum abria certo 

 

Resolvido e atualizado.

Link para o comentário
Compartilhar em outros sites

×
×
  • Criar Novo...