Reading XML files

BlitzMax Forums/BlitzMax Programming/Reading XML files

rs22(Posted 2016) [#1]
Hi,

I have started trying to use Brucey's libxml module, but I'm having trouble getting it to do what I want.

I have an XML file like the one below. I want to read it and create objects based on the type of node. Any 'type2' object can have a list of objects within it. It's for a level editor where 'type2' is an event with a list of commands to execute if the event is triggered.
<main>
	<type1/>
	<type2>
		<type1/>
		<type1/>
		<type2>
			<type1/>
			<type2>
				<type1/>
			</type2>
			<type1/>
		</type2>
	</type2>
</main>

Can anyone give me any tips? Thank you.


Derron(Posted 2016) [#2]
'load and parse file
local xmlFile:TxmlDoc = TxmlDoc.parseFile("myfile.xml")

'<main>
local rootNode:TXmlNode = xmlFile.getRootElement()
if not rootNode then Throw "<main> not found"

local subElements:TList = rootNode.GetChildren(XML_ELEMENT_NODE)
if not subElements then Throw "no children below <main>"

For local node:TXmlNode = EachIn subElements
  Select node.GetName().tolower()
    Case "type2"
       'do whatever is needed
    Case "type3"
       'do ba di ba doo
    default
       print "found unhandled node of kind ~q"+node.GetName()+"~q"
  End Select
Next


Above's code is untested, but should help to know how to handle xml content.


As LibXML returns null values if it does not find elements, you always need to ensure that the result is valid before using it ("For local node:TXmlNode = EachIn rootNode.GetChildren(XML_ELEMENT_NODE)" might segfault - if no children are present).

Therefor I created a little XML helper in my framework:
https://github.com/GWRon/Dig/blob/master/base.util.xmlhelper.bmx

Maybe you could "steal" some convenience functions from it to make life easier with libXML.


bye
Ron


rs22(Posted 2016) [#3]
Hi Derron,

Thank you for the example. What I'm really struggling with is loading an unknown amount of nested child nodes. I would show my code, but it's horrible and clearly shows I don't know what I'm doing. I know it needs some kind of recursive function, but I can't wrap my head around it. :/


Brucey(Posted 2016) [#4]
You may find the TxmlTextReader API is useful. There's a small example of it in test/test2.bmx

There's documentation on it here : http://www.xmlsoft.org/xmlreader.html
Although the docs are not specific to BlitzMax, the API should be essentially the same, and the descriptions on extracting information from nodes/attributes should translate across to the module.


Derron(Posted 2016) [#5]
Just wrap the corresponding for-eachin-loop into a function and call it recursively:

Function LoadSubElements(subElements:TList)
  if not subElements then return
  For local node:TXmlNode = EachIn subElements
    Select node.GetName().tolower()
      Case "type2"
         LoadType2Element(node)
      Case "type3"
         'do ba di ba doo
      default
         print "found unhandled node of kind ~q"+node.GetName()+"~q"
    End Select
Next
End Function


Function LoadType2Element(node:TXmlNode)
  'do custom stuff (loading special type2-nodes)

  'load other types contained in that type2
  LoadSubElements(node)
End Function


Above is just one way to do that.


bye
Ron


Brucey(Posted 2016) [#6]
Here's a small example using the xmlTextReader API in NG...

some_stuff.xml contains your xml example from above.
The output looks like:
Main
  Type1
  Type2
    Type1
    Type1
    Type2
      Type1
      Type2
        Type1
      Type1


It is non-recursive, using a stack to track parent nodes.
A node factory manages node creation.
This particular example uses NG's interfaces, but it is easy enough to write it without.


Derron(Posted 2016) [#7]
@reader
Seems a bit more complex than needed for the OP.

Nonetheless nice to see another sample for NG's interfaces.


bye
Ron


Brucey(Posted 2016) [#8]
@Reader
I've updated the example to make type1 a standard node and type2 a container type. It will now throw an exception if you try to add a node to a non-container node.

As for complex, I don't think it is. Most of the example code comprises the type stuff at the end - in order to make the example complete.

The reader-specific stuff is in the while loop, and most of that is code to handle the stack. Sure, you could call something recursively if you really wanted, but this example shows that you don't need to if you don't want to :-)


rs22(Posted 2016) [#9]
Thank you both! I think I have gotten it working how I want to.