Write to an xml file.

Posted by Met_fredrik on Sat 11 Nov 2006 11:46 PM — 11 posts, 36,089 views.

#0
Hey, as the title says I am going to write to an xml file(edit) with lua. If I got something like this code in a file called summon.xml,

<summons>
<summon name="Dog" mana="220"/>
<summon name="Rabbit" mana="220"/>
<summon name="Deer" mana="260"/>
<summon name="Pig" mana="255"/>
</summons>

How can I add a new line before the last line </summons> in the easiest possible way?

Thanks in advance.
Australia Forum Administrator #1
The easiest possible way is probably to simply read (and make a copy of) the file until you reach the line with "</summons>" on it, and write the new line before it.

A more sophisticated way would be to actually parse the XML into a table and then write it out again, but I suppose that is not the "easiest possible" way.

MUSHclient has an XML parser built in, see the function utils.xmlread on the page:

http://www.gammon.com.au/scripts/doc.php?general=lua
#2
Thanks.

I need the parsing it self to be done with pure lua, not within the MUSHclient. Got any code examples for that?

Lua is able to parse simple xml into tables without addons right?
Amended on Sun 12 Nov 2006 12:32 PM by Met_fredrik
Australia Forum Administrator #3
Not exactly. There is a reference to an XML parser in the Programming In Lua book:

http://www.lua.org/pil/29.2.html

However I think that relies upon an external parser written in C.

In your case, you seem to be trying to keep data related to a MUD in a file. Why not just use straight Lua tables, serialized to disk?

It could look like this:


summons = {}
summons.Dog = 220
summons.Rabbit = 220
summons.Deer = 260
summons.Pig = 255

require "serialize"

a = serialize.save ("summons"))

print (a)


This will print out the above table. You could instead write it to disk (that is, write variable "a" to a disk file).

Then you load it back in like this:


loadstring (a) ()


This is a lot simpler than parsing XML, and is one of the things Lua was designed to do.

Australia Forum Administrator #4
If you want to store more stuff than just mana, just make subtables, like this:


summons = {}
summons.Dog = {}
summons.Dog.mana = 220
summons.Dog.hp = 40

summons.Rabbit = {}
summons.Rabbit.mana = 220
summons.Rabbit.hp = 50

summons.Deer = {}
summons.Deer.mana = 260
summons.Deer.hp = 70

summons.Pig = {}
summons.Pig.mana = 255
summons.Pig.hp = 80


The loading and saving would be the same.
#5
The data is allready stored in xml files, I just want the possibility of changing the xml files using lua. Etc, using an item ingame wich changes the xml file that is controlling what summons the player can have.

If you understand what I mean:)

Btw, is the xml parser in c you linked for, an external program, like I have to have it in the background? Or do I have to implement it into the game server?

Thankyou very much for answers so far:)
Amended on Sun 12 Nov 2006 08:19 PM by Met_fredrik
Australia Forum Administrator #6
It was an external program, I think, I haven't used it. It would need to be a DLL or shared library.

However if your file consists of the exact text you showed there (or similar) then you could make a cut-down XML parser in straight Lua. Here is an example:


xmltext = [[
<summons>
<summon name="Dog" mana="220"/>
<summon name="Rabbit" mana="220"/>
<summon name="Deer" mana="260"/>
<summon name="Pig" mana="255"/>
</summons>
]]

xmltable = {}

function analyse_tag (name, params)
  local item = { _name = name }
  table.insert (xmltable, item)

  -- process invididual elements

  string.gsub (params, '(%a+)%s*=%s*"(.-)"', 
               function (n, v)
                 item [n] = v
               end -- function
               ) 

end -- analyse_tag 

-- process all text

string.gsub (xmltext, "<(%a+) (.-)/>", analyse_tag) 


require "tprint"
tprint (xmltable)


This code analyzes some XML text (you would write your own code to read the file, which is a couple of lines), and when run it puts it into a table. The "tprint" function in the last couple of lines is just for debugging to show what it has done, namely:


1:
  "_name"="summon"
  "name"="Dog"
  "mana"="220"
2:
  "_name"="summon"
  "name"="Rabbit"
  "mana"="220"
3:
  "_name"="summon"
  "name"="Deer"
  "mana"="260"
4:
  "_name"="summon"
  "name"="Pig"
  "mana"="255"



Now you could add to that table, and write it back as XML easily enough, like this:


for _, v in ipairs (xmltable) do
 
  line = "<" .. v._name

  for tag, contents in pairs (v) do
    if tag ~= "_name" then
      line = line .. " " .. tag .. '="' .. contents .. '"'
    end -- not _name
  end -- for
  
  line = line .. "/>"

  print (line)
end -- for loop


Running this on the above table gives:


<summon name="Dog" mana="220"/>
<summon name="Rabbit" mana="220"/>
<summon name="Deer" mana="260"/>
<summon name="Pig" mana="255"/>


Now all you have to do is put <summons> and </summons> around it.

This XML parser is fairly simple, for example it doesn't handle things like &, < or > inside the text, although that would be easy enough to do.
USA #7
This page has lots of information on handling XML from Lua. In particular it includes a Lua-only parser. However, I'm not sure if that code is 5.1, 5.0, or from before; I didn't really look at it beyond seeing it was a Lua-only parser.

http://lua-users.org/wiki/LuaXml
Australia Forum Administrator #8
Looks like Roberto's version would be much more sophisticated than mine, and is reasonably short, too.

I was giving an example of how you can use Lua to quickly parse a given text file (in this case, simple XML). That link is a great source of more complex parsers, which would apply to "real-world" XML.

I little while back I did a variant on the simple code above to parse the SMAUG help files (and commands file) as they have a simple format, and this sort of parser is perfectly adequate in those cases.
Australia Forum Administrator #9
By the way, I discovered a new feature hidden away in Lua 5.1, namely that you can use string.gsub to do a replacement using a table. Here is an example of implementing the HTML fixup (< becomes &lt; and so on) using a simple function:


function fixhtml (s)
return (string.gsub (tostring (s), "[<>&]", 
        { ["<"] = "&lt;", [">"] = "&gt;", ["&"] = "&amp;" }
       ))
end -- fixhtml

-- test

print (fixhtml ("<dog> & <cat>"))  --> &lt;dog&gt; &amp; &lt;cat&gt;

Amended on Tue 14 Nov 2006 08:02 PM by Nick Gammon
#10
So much answers! I am overwhelmed! I will get on to it right away! Thanks for all answers:)