XML Parsing within AndEngine

  ... tutorials on how to use AndEngine.

XML Parsing within AndEngine

Postby Mimminito » Sun Aug 15, 2010 8:17 pm

Hello again for another tutorial. This time it is going to be on XML Parsing, and looking at how to use it within a Level Editor/Game for loading in your levels from XML!!! Just so its clear, this tutorial is on how to Parse from XML into your AndEngine application. NOT how to save to XML (that's for another tutorial...).

First things first, check out the following link. It explains why we are using SAX to parse our XML instead of other methods:

http://www.developer.com/ws/article.php/10927_3824221_2/Android-XML-Parser-Performance.htm

Right, now onto the code! The first, and main, file we are going to look at is the XMLParser class. This is the class which will actually do the parsing of the XML for you, and where you will handle the code for what you want the Parser to actually do with the XML! Ill post the full class up first, and then will go through and explain it after.

Syntax: [ Download ] [ Hide ]
Using java Syntax Highlighting
  1. package com.mimminito.tutorial;
  2.  
  3. import org.anddev.andengine.util.SAXUtils;
  4. import org.xml.sax.Attributes;
  5. import org.xml.sax.SAXException;
  6. import org.xml.sax.helpers.DefaultHandler;
  7.  
  8. import com.badlogic.gdx.math.Vector2;
  9. import com.mimminito.box2dleveleditor.constants.MinimizeXMLConstants;
  10. import com.mimminito.box2dleveleditor.shapes.MinimizeLine;
  11. import com.mimminito.box2dleveleditor.shapes.MinimizeRect;
  12.  
  13. public class XMLParser extends DefaultHandler implements XMLConstants
  14. {
  15.         // ===========================================================
  16.         // Fields
  17.         // ===========================================================
  18.        
  19.         @SuppressWarnings("unused")
  20.         private boolean mInLevel;
  21.         @SuppressWarnings("unused")
  22.         private boolean mInLine;
  23.        
  24.         private MinimizeLevel mMinimizeLevel;
  25.        
  26.         private final StringBuilder mStringBuilder = new StringBuilder();
  27.        
  28.         public MinimizeXMLParser()
  29.         {
  30.                
  31.         }
  32.        
  33.         // ===========================================================
  34.         // Getter & Setter
  35.         // ===========================================================
  36.        
  37.         public MinimizeLevel getMinimizeLevel()
  38.         {
  39.                 return this.mMinimizeLevel;
  40.         }
  41.        
  42.         // ===========================================================
  43.         // Methods for/from SuperClass/Interfaces
  44.         // ===========================================================
  45.        
  46.         @Override
  47.         public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException
  48.         {
  49.                 if(localName.equals(TAG_LEVEL))
  50.                 {
  51.                         this.mInLevel = true;
  52.                         this.mMinimizeLevel = new MinimizeLevel(SAXUtils.getIntAttributeOrThrow(attributes, TAG_LEVEL_ATTRIBUTE_WIDTH), (SAXUtils.getIntAttributeOrThrow(attributes, TAG_LEVEL_ATTRIBUTE_HEIGHT)));
  53.                 }
  54.                 else if(localName.equals(TAG_LINE))
  55.                 {
  56.                         this.mInLine = true;
  57.                         this.mMinimizeLevel.addLine(new MinimizeLine(SAXUtils.getIntAttributeOrThrow(attributes, TAG_LINE_ATTRIBUTE_X1), SAXUtils.getIntAttributeOrThrow(attributes, TAG_LINE_ATTRIBUTE_Y1), SAXUtils.getIntAttributeOrThrow(attributes, TAG_LINE_ATTRIBUTE_X2), SAXUtils.getIntAttributeOrThrow(attributes, TAG_LINE_ATTRIBUTE_Y2), SAXUtils.getIntAttributeOrThrow(attributes, TAG_LINE_ATTRIBUTE_WIDTH)));
  58.                 else
  59.                 {
  60.                         throw new SAXException("Unexpected start tag: '" + localName + "'.");
  61.                 }
  62.         }
  63.        
  64.         @Override
  65.         public void characters(final char[] pCharacters, final int pStart, final int pLength) throws SAXException
  66.         {
  67.                 this.mStringBuilder.append(pCharacters, pStart, pLength);
  68.         }
  69.        
  70.         @Override
  71.         public void endElement(String uri, String localName, String qName) throws SAXException
  72.         {              
  73.                 if(localName.equals(TAG_LEVEL))
  74.                 {
  75.                         this.mInLevel = false;
  76.                 }
  77.                 else if(localName.equals(TAG_LINE))
  78.                 {
  79.                         this.mInLine = false;
  80.                 }
  81.                 else
  82.                 {
  83.                         throw new SAXException("Unexpected end tag: '" + localName + "'.");
  84.                 }
  85.                 this.mStringBuilder.setLength(0);
  86.         }
  87. }
  88.  
Parsed in 0.017 seconds, using GeSHi 1.0.8.4


The XMLParser class extends from the DefaultHandler class. This is a SAX class, which allows you to use the class as a handler to parse the XML code you supply.

Next you can see that there is a default constructor, with nothing in it as no real setup is required here. You may find you need to place something in here, or want to pass something into the handler so that it can perform a specific task, but thats outside the scope of this tutorial.

The next method is a getter for the MinimizeLevel (the Level class for my Editor/Game). As you will see later on, the MinimizeLevel will get created and various actions performed within the parsing on the Level. When its done, we are going to want to access the Level so that we can use what we have created!

Now onto the good stuff, the Parsing! There are two main methods for this, startElement() and endElement(). As you can probably guess, startElement() is for the start of the XML Tag, and endElement() is when its done with that Tag. I will post an example XML file up now just for reference, so you know what we are going to be parsing!

Syntax: [ Download ] [ Hide ]
Using xml Syntax Highlighting
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <level width="2000" height="320">
  3. <line x1="200" y1="320" x2="300" y2="200" width="3" />
  4. <line x1="300" y1="200" x2="700" y2="200" width="3" />
  5. <line x1="700" y1="200" x2="700" y2="320" width="3" />
  6. <line x1="1000" y1="200" x2="1900" y2="320" width="3" />
  7. </level>
Parsed in 0.001 seconds, using GeSHi 1.0.8.4


This is a simple level, which will set its width and height, and then draw 4 lines.

Right back to the XML Parser Class. Below is the startElement() method:

Syntax: [ Download ] [ Hide ]
Using java Syntax Highlighting
  1. @Override
  2. public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException
  3. {
  4.         if(localName.equals(TAG_LEVEL))
  5.         {
  6.                 this.mInLevel = true;
  7.                 this.mMinimizeLevel = new MinimizeLevel(SAXUtils.getIntAttributeOrThrow(attributes, TAG_LEVEL_ATTRIBUTE_WIDTH), (SAXUtils.getIntAttributeOrThrow(attributes, TAG_LEVEL_ATTRIBUTE_HEIGHT)));
  8.         }
  9.         else if(localName.equals(TAG_LINE))
  10.         {
  11.                 this.mInLine = true;
  12.                 this.mMinimizeLevel.addLine(new MinimizeLine(SAXUtils.getIntAttributeOrThrow(attributes, TAG_LINE_ATTRIBUTE_X1), SAXUtils.getIntAttributeOrThrow(attributes, TAG_LINE_ATTRIBUTE_Y1), SAXUtils.getIntAttributeOrThrow(attributes, TAG_LINE_ATTRIBUTE_X2), SAXUtils.getIntAttributeOrThrow(attributes, TAG_LINE_ATTRIBUTE_Y2), SAXUtils.getIntAttributeOrThrow(attributes, TAG_LINE_ATTRIBUTE_WIDTH)));
  13.         else
  14.         {
  15.                 throw new SAXException("Unexpected start tag: '" + localName + "'.");
  16.         }
  17. }
Parsed in 0.011 seconds, using GeSHi 1.0.8.4


This method is Overidden so that you can handle your specific tags within your XML file. So, first off we check the localName attribute, and see if it is equal to one of our Tags. In this case, its the TAG_LEVEL tag (these values are defined in a separate file I have called MinimizeConstants). If we find this tag, then we have to deal with it accordingly. As you can see, in my Level Editor if I find the TAG_LEVEL tag, then I create a new Level and set it to the one we are storing in this class.

Now, this is an important bit. If you have a Tag, you are supposed to do something when you find it. At the top of this class, you may have noticed the boolean Fields that were created, and given the suppressed warnings. These are what we use to identify if we are within a Tag. When we are finished with a Tag, we can then set them to false again, and we can move onto the next. These can also be very handy when say you have some elements within another element in the XML file. You can then check to see if you are in its parent, and if so deal with the next element.

Ok, so once we have dealt with the TAG_LEVEL tag, we move onto the TAG_LINE tag. This is the same principle, where if we find this Tag, we create a new Line and add it into the Level we have stored.

Now, after the startElement() method has found a start of a Tag, once its done it checks for the end of the Tag. This is where the endElement() method comes into play:

Syntax: [ Download ] [ Hide ]
Using java Syntax Highlighting
  1. @Override
  2. public void endElement(String uri, String localName, String qName) throws SAXException
  3. {              
  4.         if(localName.equals(TAG_LEVEL))
  5.         {
  6.                 this.mInLevel = false;
  7.         }
  8.         else if(localName.equals(TAG_LINE))
  9.         {
  10.                 this.mInLine = false;
  11.         }
  12.         else
  13.         {
  14.                 throw new SAXException("Unexpected end tag: '" + localName + "'.");
  15.         }
  16.         this.mStringBuilder.setLength(0);
  17. }
Parsed in 0.010 seconds, using GeSHi 1.0.8.4


This acts in the same way as the startElement() method, but instead of looking for a start of a Tag, it looks for the end. You can do some processing within here if you wish, but I do not. I find it easier to just do it within the startElement() method.
BUT, we do have to set the boolean value for that Tag to false, so that the class knows we are not within it anymore.

And thats about it for this class. Pretty simple hey? All that is really left to say, is the for both the startElement() and endElement() methods, if there is a Tag that it does not recognise, or some bad XML code, then it will throw a SAXException detailing which Tag it does not like!

Ok, I think that is enough for this one post. Take a look at this, and go away and create your own XMLParser class. Once you are done, come back and check out the second part of this tutorial in the post below...

(On a side not, if there is anything in here you feel is incorrect, or you want added in, highlight the issue and let me know. I shall amend as required)
---------------------------------------
Adam Goodchild
Your AndEngine Forum Moderator!
My Tutorials List
http://www.adam-goodchild.co.uk
---------------------------------------
Mimminito
 
Posts: 360
Joined: Wed Jul 21, 2010 3:08 pm
Location: Chelsmford, UK

XML Parsing within AndEngine Part 2

Postby Mimminito » Sun Aug 15, 2010 8:17 pm

Right, part two of this tutorial.

In the first we talked about creating the actual XMLParser class, which will handle the XML file for us. The next class we need to create is the Loader class (or in my Level Editor's case, the MinimizeLevelLoader class). This is the class that will do the dirty work for you, and save you some time when creating your applications. Like before, ill post the full class below then talk about what it does:

Syntax: [ Download ] [ Hide ]
Using java Syntax Highlighting
  1. package com.mimminito.box2dleveleditor;
  2.  
  3. import java.io.BufferedInputStream;
  4. import java.io.IOException;
  5. import java.io.InputStream;
  6.  
  7. import javax.xml.parsers.ParserConfigurationException;
  8. import javax.xml.parsers.SAXParser;
  9. import javax.xml.parsers.SAXParserFactory;
  10.  
  11. import org.xml.sax.InputSource;
  12. import org.xml.sax.SAXException;
  13. import org.xml.sax.XMLReader;
  14.  
  15. import android.content.Context;
  16.  
  17. public class MinimizeLevelLoader
  18. {
  19.         // ===========================================================
  20.         // Methods
  21.         // ===========================================================
  22.        
  23.         public MinimizeLevel loadLevelFromAsset(final Context pContext, final String pAssetFilePath) throws SAXException
  24.         {
  25.                 try
  26.                 {
  27.                         final SAXParserFactory spf = SAXParserFactory.newInstance();
  28.                         final SAXParser sp = spf.newSAXParser();
  29.                        
  30.                         final XMLReader xmlReader = sp.getXMLReader();
  31.                         final MinimizeXMLParser minimizeXMLParser = new MinimizeXMLParser();
  32.                         xmlReader.setContentHandler(minimizeXMLParser);
  33.                        
  34.                         InputStream inputStream = pContext.getAssets().open(pAssetFilePath);
  35.                         xmlReader.parse(new InputSource(new BufferedInputStream(inputStream)));
  36.                        
  37.                         return minimizeXMLParser.getMinimizeLevel();
  38.                 }
  39.                 catch (SAXException e)
  40.                 {
  41.                         throw new SAXException(e);
  42.                 }
  43.                 catch (ParserConfigurationException e)
  44.                 {
  45.                         e.printStackTrace();
  46.                 }
  47.                 catch (IOException e)
  48.                 {
  49.                         e.printStackTrace();
  50.                 }
  51.                 return null;
  52.         }
  53. }
  54.  
Parsed in 0.012 seconds, using GeSHi 1.0.8.4


This class has no constructor, as you do not have to do anything in it. All it has is a loader method, which I'm sure you have already guessed, loads the Level from the XML file for us!

This method takes in two parameters, the Context that the method is being called from, and the file path for the XML file you want to parse.

Now this is where SAX is very useful, making our lives easier. Below is the code which actually does the work for you:

Syntax: [ Download ] [ Hide ]
Using java Syntax Highlighting
  1. final SAXParserFactory spf = SAXParserFactory.newInstance();
  2. final SAXParser sp = spf.newSAXParser();
  3.  
  4. final XMLReader xmlReader = sp.getXMLReader();
  5. final MinimizeXMLParser minimizeXMLParser = new MinimizeXMLParser();
  6. xmlReader.setContentHandler(minimizeXMLParser);
  7.  
  8. InputStream inputStream = pContext.getAssets().open(pAssetFilePath);
  9. xmlReader.parse(new InputSource(new BufferedInputStream(inputStream)));
  10.  
  11. return minimizeXMLParser.getMinimizeLevel();
Parsed in 0.010 seconds, using GeSHi 1.0.8.4


First we create a new instance of the SAXParserFactory. This then allows us to create the actual SAXParser that we will use to parse our file.
Once that it created, we then create a new XMLReader object, which as you can guess, reads the XML for us. This is taken from the SAXParser we had just created.
Now we create a new instance of our XMLParser class. Once this is done, we set this as the XMLReader's Content Handler (hence why we extended our XML Parser class from the DefaultHandler class).

Now that we have done the initialization for the XML Parsing, we need to access the file from the device. So we create a new InputStream, and get the Level from where it is stored. TAKE NOTE HERE, the method is called loadLevelFromAsset, which is exactly what we are doing. The Level has to be stored in the Asset's folder of your project for this to work. This is not to say you cannot access the XML file from another source, such as your SD card. In that case, you would create another method called loadLevelFromExternal, and then access the file from the External Storage.

Ok, so now that we have our XML file, and that we have setup everything, we can Parse the file, and get our level out. we call the XMLReaders parse() method, which goes through all of our Tags that we defined in the XMLParser class, and does what we asked it to do.
The last bit is simple. We return the newly created Level by calling our getter from the XMLParser class!

And that's it, you are now able to parse your own XML files at ease. Feel free to copy and paste these classes into your own projects, and change the names to suit your own needs. All I ask for is a sweet little thank you as a reply to this tutorial.

Lastly, I cant leave you without showing you how to actually use the LevelLoader class can I? So below is the code you would use to access the Level, and store it within your Game:

Syntax: [ Download ] [ Hide ]
Using java Syntax Highlighting
  1. MinimizeLevel level;
  2. final MinimizeLevelLoader minLevelLoader = new MinimizeLevelLoader();
  3. level = minLevelLoader.loadLevelFromAsset(this, "levels/TestLevel.xml");
Parsed in 0.011 seconds, using GeSHi 1.0.8.4


Yup, its as simple as that. You now have your newly created Level from your XML file!

I hope this helps a few people. Rather than replying to threads about how to do stuff, I intend to create as many tutorials as I can so people have more than just the Examples to look at.

Again, anything which is a bit off, or you feel should be added in or changed, please let me know and I shall amend as required.
---------------------------------------
Adam Goodchild
Your AndEngine Forum Moderator!
My Tutorials List
http://www.adam-goodchild.co.uk
---------------------------------------
Mimminito
 
Posts: 360
Joined: Wed Jul 21, 2010 3:08 pm
Location: Chelsmford, UK

Re: XML Parsing within AndEngine

Postby blandonfrank » Sun Aug 15, 2010 9:22 pm

Adam,

Thanks a lot for this tutorial! You've saved me a ton of time. :)
blandonfrank
 
Posts: 298
Joined: Fri Jul 23, 2010 9:46 pm

Re: XML Parsing within AndEngine

Postby Mimminito » Sun Aug 15, 2010 9:29 pm

No problem.
---------------------------------------
Adam Goodchild
Your AndEngine Forum Moderator!
My Tutorials List
http://www.adam-goodchild.co.uk
---------------------------------------
Mimminito
 
Posts: 360
Joined: Wed Jul 21, 2010 3:08 pm
Location: Chelsmford, UK

Re: XML Parsing within AndEngine

Postby bhecox65 » Tue Aug 24, 2010 3:25 am

:o :D This is awesome! Thanks for the tutorial! It's really helpful, I would have had a few headaches trying to figure that all out myself! Haha.

Oh, do you think you or Nicolas, or anyone that knows how, could explain how you then really create the objects after they are loaded into the level. Meaning how do we add the object to the scene and to the physics world? I might figure it out, but it would be really nice to know how the pros would go about doing it! :lol: For efficiency sake especially.

Also, and probably more importantly, how do we go about saving to xml in the level editor?

Thanks for all your help guys! :D
------------------------------------------------------------
Check out my site: http://www.debragames.com
I have one Android game out: Tap It!

It's free so download it now! :D
bhecox65
 
Posts: 31
Joined: Sun Aug 08, 2010 11:09 pm

Re: XML Parsing within AndEngine

Postby Mimminito » Tue Aug 24, 2010 2:04 pm

Well there are a good few posts on the forum about how you can add objects into AndEngine. I would first check out the Physics Example online, or download it so you can play with it more easily. Once you have a feel for how you create objects in AndEngine, and how you add them to the Physics World and Scene, then move on.

The way that I originally added Objects into my Scene wa perfect... till I realised that I was unable to re-add them to the PhysicsWorld without a lot of hassle. What I have done/am in the middle of doing now, is to create a Helper Class, which creates standard Objects and Physics Objects. I can then call these from any class I like (as its a static helper class) and I can create my objects.

My real aim is to complete my Level Editor to a decent standard, and then produce a full set of tutorials on how you would go about creating your own. I think its something that is very valuable to a game (especially on a mobile device) but is lacking in tutorials and help on the Internet. Hopefully it can be a source of attraction to more game developers that they can come over and use AndEngine to create something even more awesome!!!!
(But this is all a dream until its done :D)
---------------------------------------
Adam Goodchild
Your AndEngine Forum Moderator!
My Tutorials List
http://www.adam-goodchild.co.uk
---------------------------------------
Mimminito
 
Posts: 360
Joined: Wed Jul 21, 2010 3:08 pm
Location: Chelsmford, UK

Re: XML Parsing within AndEngine

Postby bhecox65 » Wed Aug 25, 2010 2:18 am

Ah, ok, well good luck with the tutorials and helper class! hopefully you can get them out soon. :D I am really hoping to have a level editor for my game as well, and the tutorials would be extremely helpful.

Also, my real question was not how do you add objects to the scene/world, because I know how to do that. But, what I am not sure about is how you add the objects from the information the xml file gives your level. You show at the end there that you have a MinimizeLevel class, and I assume that's where you actually create the objects based on the info from the MinimizeLevelLoader. I think if you simply posted the MinimizeLevel class it would explain what I don't understand.

Well, thanks again for you help!
------------------------------------------------------------
Check out my site: http://www.debragames.com
I have one Android game out: Tap It!

It's free so download it now! :D
bhecox65
 
Posts: 31
Joined: Sun Aug 08, 2010 11:09 pm

Re: XML Parsing within AndEngine

Postby Mimminito » Wed Aug 25, 2010 9:28 pm

My Level class only stores raw information such as the locations of objects, width, height, colour. This is why I have these helper classes. You can iterate over your MinimizeLevel class and access all the information you need to create your objects.
---------------------------------------
Adam Goodchild
Your AndEngine Forum Moderator!
My Tutorials List
http://www.adam-goodchild.co.uk
---------------------------------------
Mimminito
 
Posts: 360
Joined: Wed Jul 21, 2010 3:08 pm
Location: Chelsmford, UK

Re: XML Parsing within AndEngine

Postby sPOT902 » Fri Sep 17, 2010 6:34 am

Is there any way to retrive a String or char using a sax util? like how SAXUtils.getIntAttributeOrThrow gets an int, is there a SAXUtils.getCharAttributeOrThrow or SAXUtils.getStringAttributeOrThrow? i had a look but couldn't find any. I want to parse an xml file for my space shooter with lines containing the enemies first letter ie: 'D' for drone, so my level knows what type of enemy to add at what time etc.
sPOT902
 
Posts: 37
Joined: Tue Aug 24, 2010 12:48 pm

Re: XML Parsing within AndEngine

Postby soshimo » Fri Sep 17, 2010 6:08 pm

Oh man, I don't miss event driven xml parsing ala SAX. This is good though, good introduction to the java side of things (I played with xerces in c++).

One quick question, how do you deal with entities? I had a bear of a time dealing with them and ended up having to include funky states so my parser would know that it was in the middle of parsing an entity. In my example I would get a startElement for the beginning of the xml element and if there was an entity in the xml text I would get ANOTHER startElement for the entity.

For example,
Syntax: [ Download ] [ Hide ]
Using xml Syntax Highlighting
  1. <sometag name="somename">&amp;</sometag>
Parsed in 0.000 seconds, using GeSHi 1.0.8.4


In this case I would get a startElement for sometag and then another startElement for the entity itself. Maybe that was a xerces implementation detail though so you might not have that issue with the android sax parser.
soshimo
 
Posts: 11
Joined: Thu Sep 09, 2010 6:50 am

Next

Return to Tutorials

Who is online

Users browsing this forum: Google [Bot] and 5 guests