Reading xml?

Monkey Forums/Monkey Programming/Reading xml?

Spinal(Posted 2013) [#1]
Hi all.

I'm currently using the skn3/config example to read my level data from an xml file, but have a question regarding reading some of the values

The xml data is from a .tmx (tiled) file
<map version="1.0" orientation="orthogonal" width="80" height="30" tilewidth="10" tileheight="10">
 <tileset firstgid="1" name="mytiles" tilewidth="10" tileheight="10">
  <image source="tileset.png" width="80" height="640"/>
 </tileset>
 <layer name="Level" width="80" height="30">
  <data encoding="csv">
.....some data
</data>
 </layer>
 <objectgroup name="Object Layer 1" width="80" height="30">
  <object name="Button" type="dButton" x="540" y="270" width="10" height="10">
   <properties>
    <property name="Number" value="1"/>
   </properties>
  </object>
 </objectgroup>
</map>


I'm currently reading the data like this--
	nodes = c1.FindNodesByPath("map/objectgroup/object")
	For Local node := Eachin nodes
		Select node.GetAttribute("name")
			Case "Button"
				If node.GetAttribute("type")="dButton" Then 'Door button
					DoorBX[NumDoorButtons]=Int(node.GetAttribute("x"))
					DoorBY[NumDoorButtons]=Int(node.GetAttribute("y"))
					NumDoorButtons = NumDoorButtons + 1
				End
		End Select
	Next	


yes, I know I shouldn't really be using arrays for holding the data, but still...

How do I go about reading the "Number" property for the object type "bDutton"?

Thanks. :)


ElectricBoogaloo(Posted 2013) [#2]
Eeuw, XML! Eeeeuuww!!!
I think you've over complicate it by adding multiple parameters in each line.
The last time I tried anything with XML I kept everything seperate..
<object>
  <name>Giant Long Thing with Tentacles</name>
  <width>128</width>
  <height>64</height>
</object>


Since you're having to code the parser, it just seems easier to format the data in a way that's more readable.
.. To be honest, I prefer the .ini format, myself.

[Object]
Name=Giant Long Thing with Tentacles
Width=128
Height=64

Then you can simply use Monkey's Split function to split up each line where the = is, and easily access each parameter.
... But that's just me!!


Spinal(Posted 2013) [#3]
I was hoping not to have to create my own level format and just use something already available. A .tmx file is perfect(ish) as I can just keep the saved file directly from tiled.


ElectricBoogaloo(Posted 2013) [#4]
My answer is quick, simple, and kinda dirty. .. but I imagine you expected as much..

I wrote a quick and dirty SeekXML function.
	Method SeekXML$(ftxt$,fseek$)
	
	Local ff,fa,fb,fn,ftmp$,ftxtUpper$=ftxt.ToUpper()
		fseek=" "+fseek.ToUpper()+"="
		ff=ftxtUpper.FindLast(fseek)
		If ff>-1
			fa=ftxt.Find(ftmp.FromChar(34),ff+1)
			fb=ftxt.Find(ftmp.FromChar(34),fa+1)
			If fa>-1 And fb>-1
				For fn=fa+1 To fb-1
				ftmp=ftmp+ftxt.FromChar(ftxt[fn])
				Next
			Endif
			Return ftmp
		Endif
		If ff=-1 Then Return "-=-"
	End Method


Feel free to bombard me with "You could've done it XYZ way!!" complaints, people..

Usage.. I'm assuming you can gain access to each individual line of XML. If not.. umm.. urrr.. You're buggered!!


Local txt$
Local c1$="<object name="+txt.FromChar(34)+"Button"+txt.FromChar(34)+" type="+txt.FromChar(34)+"dButton"+txt.FromChar(34)+" x="+txt.FromChar(34)+"540"+txt.FromChar(34)+" y="+txt.FromChar(34)+"270"+txt.FromChar(34)+" width="+txt.FromChar(34)+"10"+txt.FromChar(34)+" height="+txt.FromChar(34)+"10"+txt.FromChar(34)+">"

Text 100,80,SeekXML(c1,"Name")
Text 100,100,SeekXML(c1,"X")
Text 100,120,SeekXML(c1,"Y")
Text 100,140,SeekXML(c1,"Width")
Text 100,160,SeekXML(c1,"Height")
Text 100,180,SeekXML(c1,"Pants")

c1="<objectgroup name="+txt.FromChar(34)+"Object Layer 1"+txt.FromChar(34)+" width="+txt.FromChar(34)+"80"+txt.FromChar(34)+" height="+txt.FromChar(34)+"30"+txt.FromChar(34)+">"

Text 200,80,SeekXML(c1,"Name")
Text 200,100,SeekXML(c1,"X")
Text 200,120,SeekXML(c1,"Y")
Text 200,140,SeekXML(c1,"Width")
Text 200,160,SeekXML(c1,"Height")
Text 200,180,SeekXML(c1,"Pants")



Spinal(Posted 2013) [#5]
unfortunately I can do that with the current functions -

nodes = c1.FindNodesByPath("map/objectgroup/object/properties/property")

will give me the information for all of the properties, however, what I want doesn't seem to be a function - reading ONLY the property tag that falls within the CURRENT node.

I would like OtherData[NumDoorButtons] to grab the property value from the current "Button", not a list of all properties that I can't relate in any way back to the data that I am trying to connect it to.

	nodes = c1.FindNodesByPath("map/objectgroup/object")
	For Local node := Eachin nodes
		Select node.GetAttribute("name")
			Case "Button"
				If node.GetAttribute("type")="dButton" Then 'Door button
					DoorBX[NumDoorButtons]=Int(node.GetAttribute("x"))
					DoorBY[NumDoorButtons]=Int(node.GetAttribute("y"))

					OtherData[NumDoorButtons] = value from property with name="Number" within CURRENT object with name="Button"

					NumDoorButtons = NumDoorButtons + 1
				End
		End Select
	Next	



AdamRedwoods(Posted 2013) [#6]
you mean this?
local nn:XMLNode = node.GetChild("properties").GetChild("property")
if nn.GetAttribute("name")="Number" Then OtherData[NumDoorButtons] = nn.GetAttribute("value")


if your current node is "object" you still need to get the child.
if you have a lot of properties, you'll have to set "nn" to a List<XMLNode> and "GetDescendents"
...or while/Wend through using nn.GetNextSibling("property").


Spinal(Posted 2013) [#7]
I tried switching to xml.monkey instead of config.monkey, however I can seem to read anything at all from my xml file :(

level01.xml
<?xml version="1.0" encoding="UTF-8"?>
<map version="1.0" orientation="orthogonal" width="80" height="30" tilewidth="10" tileheight="10">
 <tileset firstgid="1" name="mytiles" tilewidth="10" tileheight="10">
  <image source="tileset.png" width="80" height="640"/>
 </tileset>
 <layer name="Level" width="80" height="30">
  <data encoding="csv">
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,80,201,202,202,203,79,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,209,210,210,211,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,209,210,210,211,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,209,210,210,211,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,209,210,227,211,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80,201,
0,0,0,0,0,0,209,210,210,211,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,209,
0,0,0,0,0,0,209,210,210,211,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,209,
0,0,0,0,0,0,209,210,210,211,138,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,209,
0,0,0,0,0,0,209,210,210,211,46,47,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,209,
0,0,0,0,0,0,209,210,210,211,71,55,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,209,
0,0,0,0,0,0,209,210,210,211,142,139,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,209,
0,0,0,0,0,0,209,235,210,211,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,209,
0,0,0,0,0,0,209,210,210,211,0,69,70,0,0,0,0,69,70,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,209,
0,0,0,0,0,0,209,210,210,211,138,140,141,0,0,0,0,140,141,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,41,42,43,44,46,47,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,209,
0,0,0,0,0,0,209,210,210,211,0,0,0,0,0,0,0,0,0,0,0,0,0,41,42,43,44,46,47,0,0,0,0,0,0,0,0,0,0,0,76,77,49,70,51,52,71,55,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,209,
0,0,0,0,0,0,217,218,218,219,0,0,0,0,0,0,0,0,0,0,0,76,77,49,70,51,52,71,55,0,0,0,0,0,0,0,0,0,0,80,204,205,249,141,138,138,140,251,202,203,79,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,209,
0,0,0,0,0,0,0,0,84,0,0,0,0,0,0,0,0,0,0,0,80,204,205,249,141,138,138,140,251,202,203,79,0,0,0,0,0,0,0,0,0,83,0,0,0,0,0,217,218,219,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80,201,202,202,202,202,202,202,202,202,202,202,202,221,
0,0,0,0,0,0,0,0,86,0,0,0,0,0,0,0,0,0,92,93,0,0,83,0,0,0,0,0,217,218,219,0,0,0,0,0,0,0,0,0,0,85,0,0,0,0,0,0,86,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,209,212,218,218,218,218,218,218,218,218,218,213,210,
0,0,0,0,0,0,0,0,0,0,0,0,80,204,205,206,79,0,100,101,0,0,85,0,0,0,0,0,0,86,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,217,219,0,0,0,0,0,0,0,0,0,209,210,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,85,0,0,0,108,109,0,0,0,0,0,0,0,0,0,0,0,0,0,80,89,90,91,79,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,209,210,
0,0,0,0,73,74,75,0,0,0,0,0,0,0,0,0,0,0,116,117,0,76,77,0,0,175,176,0,73,74,75,76,77,0,97,227,99,0,0,0,0,0,0,0,0,0,0,0,0,0,73,74,75,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,209,210,
201,203,79,80,201,242,242,202,202,202,202,202,202,202,202,202,202,202,202,202,202,242,242,202,202,207,208,202,242,242,242,202,202,202,241,241,241,202,202,202,202,202,202,202,202,202,202,202,202,202,242,242,242,202,202,202,202,202,202,202,202,202,202,202,202,202,202,202,202,202,202,202,202,202,202,202,202,202,221,210,
209,211,169,169,209,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210
</data>
 </layer>
 <objectgroup name="Object Layer 1" width="80" height="30">
  <object name="Start" x="190" y="280"/>
  <object name="Door" type="Door" x="670" y="260" width="10" height="20">
   <properties>
    <property name="Number" value="1"/>
   </properties>
  </object>
  <object name="Door" type="Door" x="570" y="270" width="10" height="10">
   <properties>
    <property name="Number" value="2"/>
   </properties>
  </object>
  <object name="Door" type="Door" x="610" y="260" width="10" height="10">
   <properties>
    <property name="Number" value="3"/>
   </properties>
  </object>
  <object name="Button" type="dButton" x="540" y="270" width="10" height="10">
   <properties>
    <property name="Number" value="1"/>
   </properties>
  </object>
 </objectgroup>
</map>


	'load xml
	Local err:XMLError
	Local xml:= ParseXML(LoadString("level01.xml"), err)
	If xml = Null Error("doh!")
	If err <> Null Then Print err
	
	'parse xml
	Local sheet:= xml.GetChild("map").GetChild("objectgroup")
        Print "name = '"+sheet.GetAttribute("name")+"'"


Have I missed something important?


Spinal(Posted 2013) [#8]
Nobody?


computercoder(Posted 2013) [#9]
Looks like its time for me to look into writing another Tiled reader/writer but this round in Monkey. Sure, Diddy has it builtin, but I like to create my own stuff :)

I'll look into this some more, as I haven't had much exposure with Monkey yet. I'll focus on XML as my entry point into Monkey and I'll get back with you on this.

I wrote a Tiled TMX Reader/Writer for BlitzMax, C#, REALbasic (now Xojo), and Java. What's ONE more? :)

I'll keep you posted as how to handle reading the XML.

Generally, there is a GetAttrib or GetAttribute in the XML parser you are using that allows you to get the attributes. Some even break it down into Nodes, where there is a Parent Node and Child Node(s). You iterate through the Parents nodes until you reach the child nodes, and then grab the attributes accordingly.... I'll need to see how Monkey does this myself, but is shouldn't be that complicated.

EDIT: It just occured to me that you are using skn3's module to handle XML parsing. I just looked at it, and seems to be pretty straight forward. I'll have a look at it tonight. I like the idea that its all in one source. I'd love to have the writer class to go with it too :) Guess I'll add that to my TODO list too!


Skn3(Posted 2013) [#10]
Psst take a look here for a more comprehensive XML module:
http://www.monkeycoder.co.nz/Community/posts.php?topic=4125

There should be examples to cover everything you want to know.

That config module is pretty ancient now and very slow compared to the one above and one that comes with diddy.

[Edit]

Whoops seems you did switch already. Well your code is failing because the doc node is always treated as your root node. So you don't need to bother with GetChild("map")


computercoder(Posted 2013) [#11]
Skn3:

Thats the page I've been looking at. I'm *just* now sitting down to have a go at your implementation of XML parsing. I didn't really have a deep look at it earlier, but I now see that it parses and creates XML. :) Very cool. Thanks for that module too. Looking forward to seeing what I can do using it :)


RENGAC(Posted 2013) [#12]
Computercoder, I'd love to see your Tiled importer working. I'm trying to import a map by myself, but my code sucks :(


computercoder(Posted 2013) [#13]
RENGAC,

I've been busy with getting the OUYA controllers working over here with Ignition, but I'll be soon able to get this going. Perhaps in a couple days I could have the reader ready :)


RENGAC(Posted 2013) [#14]
Nice!


computercoder(Posted 2013) [#15]
Computercoder, I'd love to see your Tiled importer working. I'm trying to import a map by myself, but my code sucks :(



I sent you an email :)


Spinal(Posted 2013) [#16]
Guys, can someone tell me how to overcome the following problem?
When running the following code, to read more than one layer from my tile map, I'm getting

Monkey Runtime Error : Null object access

on the line

item = item.GetNextSibling("layer")



	'load xml
	Local err:XMLError
	Local xml:= ParseXML(LoadString("level01.tmx"), err)
	If xml = Null Error("doh! can't read xml!")
	If err <> Null Then Print err
	
	Local item:XMLNode = xml
	item = item.GetChild("layer")
	While item.valid
    	
    	Local name$ = item.GetAttribute("name")
    	Print name
		Select name
			Case "Layer1"
			    LevelHeight = Int(item.GetAttribute("height"))
			    LevelWidth = Int(item.GetAttribute("width"))
				'Grab level data
				'Print LevelWidth+","+LevelHeight
				Local LevDat:String = item.GetChild("data").value
				'Print LevDat
	
				Local x=0,y=0
				For Local tempDat$ = Eachin LevDat.Split(",")
					If x < 400 And y < 400 Then LevelMap[x +400* y]=Int(tempDat.Trim())
					x=x+1
					If x=LevelWidth Then 
						y=y+1
						x=0
					End If
				Next

			Case "Layer2" ' copy+paste of above code for now
			    LevelHeight = Int(item.GetAttribute("height"))
			    LevelWidth = Int(item.GetAttribute("width"))
				'Grab level data
				Print LevelWidth+","+LevelHeight
				Local LevDat:String = item.GetChild("data").value
				Print LevDat
	
				Local x=0,y=0
				For Local tempDat$ = Eachin LevDat.Split(",")
					If x < 400 And y < 400 Then LevelMap[x +400* y]=Int(tempDat.Trim())
					x=x+1
					If x=LevelWidth Then 
						y=y+1
						x=0
					End If
				Next

			End Select
        'next item
        item = item.GetNextSibling("layer")
       Wend


Any idea what I'm doing wrong?


Skn3(Posted 2013) [#17]
Hey it looks correct as far as I can see. Just checked the module source and its returning the nullNode properly if there is no nextsibling.

What version of the xml lib are you using?


Spinal(Posted 2013) [#18]
16 I assume. thats the first one listed at the top of the file anyway...

[edit] just noticed its up to 21, ill give that a try :)