Android SAX XML Parsing

Print Friendly

This article describes how to create an XML Parser for Android. The files are located in the SD card and use a SAX parser. Why use XML? If you are designing an application that has static data, it might be simpler to keep information in a flat file instead of creating a database and query for values.

Here is the sample XML file that we will parse:

<?xml version="1.0" encoding="UTF-8"?>
<AndroidGames>
	<GAMES>
		<Name>Super Mario Brothers</Name>
		<LocationImage>/sdcard/games/supermario.png</LocationImage>
		<Description>Mario must save the princess from Browser</Description>
	</GAMES>
	<GAMES>
		<Name>Call of Duty</Name>
		<LocationImage>/sdcard/games/callofduty.png</LocationImage>
		<Description>Play online against your friends</Description>
	</GAMES>
	<GAMES>
		<Name>Aliens vs Predator</Name>
		<LocationImage>/sdcard/games/aliensvspredator.png</LocationImage>
		<Description>They win, we lose</Description>
	</GAMES>
	<GAMES>
		<Name>The land of happiness</Name>
		<LocationImage>/sdcard/games/landofhappiness.png</LocationImage>
		<Description>Something gay</Description>
	</GAMES>
</AndroidGames>

In here I am making some app that has game information and an image location from the sd card.

Create ContentObject.java

This class contains the elements from the XML file you created. It looks something like this:

public class ContentObject {
	private String mName;
	private String imageLocation;
	private String mDescription;
	
	/**
	 * Set functions
	 */
	public void setName (String name){
		mName = name;
	}
	public void setLocationImage(String location){
		imageLocation = location;
	}
	public void setDescription(String description){
		mDescription = description;
	}
	/**
	 * Get functions
	 */
	public String getName(){
		return mName;
	}
	public String getLocationImage(){
		return imageLocation;
	}
	public String getDescription(){
		return mDescription;
	}
}

If you notice the methods all they do is set and get:
1. Name
2. LocationImage
3. Description

It is just like the XML file I created.

The setter methods will set the objects when the XML SAX parser is called. We will use the getter methods to get the information later so it can be used in our app.

Create XmlSaxParser.java

Now we create the XML parser:

public class XmlSAXParser extends DefaultHandler{
	private String TAG = "JUAN";
	private String mLocation;
	private String mSelection;
	public List<ContentObject> myContentObjects = new ArrayList<ContentObject>();
	private String tempVal;

	private ContentObject tempContentObject;

	/**constructor that sets the file Location (mLocation) and mSelection(GAMES in this case)*/
	public XmlSAXParser(String location, String selection){
		mLocation = location;
		mSelection = selection;
	}

	/**the heart of the parser. Initializes a SAXParserFactory and
	 *  uses the java classes to parse my file*/
	public void runParser(){
		try{
			SAXParserFactory spf = SAXParserFactory.newInstance();
			SAXParser sp = spf.newSAXParser();
			XMLReader xr = sp.getXMLReader();
			File file = new File(mLocation);
			FileInputStream fis = new FileInputStream(file);
			xr.setContentHandler(this);
			xr.parse(new InputSource(fis));
		} catch (Exception e) {
			Log.i(TAG,"XML Pasing Excpetion = " + e);
			System.exit(-1);
		}
	}

	/**These classes below are my event handlers
	 * When the xr.parse above takes effect, the startElement and endElement
	 * will grab the data and put it in my created class ContentObject*/
	
	/**when the start Element is found to be "GAMES", I will create a new content object to hold
	 * my elements inside*/
	public void startElement
	(String uri, String localName, String qName, Attributes attributes) throws SAXException {
		if (localName.equals(null)){
			Log.i(TAG,"qName is empty");
		}
		tempVal = "";
		if(localName.equalsIgnoreCase(mSelection)) {
			/**create a new ContentObject to hold all XML elements*/
			tempContentObject = new ContentObject();
		}
	}

	public void characters(char[] ch, int start, int length) throws SAXException {
		tempVal = new String(ch,start,length);
	}
	/**Once the end tag is found in XML, I write the element's data to the content object
	 * If "GAMES" ending tag is found, then I add to the List of my content objects*/
	public void endElement(String uri, String localName, String qName) throws SAXException {
		/**add the content object with the elements inside to my List of Content objects*/
		if(localName.equalsIgnoreCase(mSelection)) {
			myContentObjects.add(tempContentObject);
		}else if (localName.equalsIgnoreCase("Name")) {
			tempContentObject.setName(tempVal);
		}else if (localName.equalsIgnoreCase("LocationImage")) {
			tempContentObject.setLocationImage(tempVal);
		}
		else if (localName.equalsIgnoreCase("Description")){
			tempContentObject.setDescription(tempVal);
		}
	}

	/**I return a List of ContentObjects to be ready to be itterated*/
	public List<ContentObject> getParsedData(){
		return this.myContentObjects;
	}
}

Here we call a factory that returns a SAXParser instance, wrap our string as a file that is put into our parser and we parse (xr.parse()).

The methods startElement and endElement are called when in our XLM file an element tag is called. This parser here works with Element tags. If you add attributes, the code will have to be slightly modified. If you don’t know the difference between elements and attributes, just write everything like elements ( like I did above in my XML file) if its a simple XML file.
When the mSelection START TAG is found, I create a ContentObject object. This is then used to SET the name, locationimage and
description. the mSelection tag is “GAMES” because that is what my main tag to distinguish elements in my XML and you will see pretty
soon. When mSelection ENDING TAG is found, then I add that ContentObject object to a list of ContentObjects. So for this XML file, I will
have a List of 4 ContentObjects which contain all the information I collected.

NOTE: Why SAX? DOM keeps the whole file in memory. For an android device (embedded system) (your phone!) you don’t want to do that, so I choose SAX which is object modelled.

Create TestActivity.java

Create an Activity that will grab the information from your objects to whatever you want. In my case, I just display it using Logcat:

public class TestActivity extends Activity {
	/** Called when the activity is first created. */
	private final String TAG = "TAG";
	
	@Override
	public void onCreate(Bundle savedInstanceState) {
		
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		
		
		/**I instantiate my class XmlSAXParser and give it the file location and an start element tag*/
		XmlSAXParser myXMLParser = new XmlSAXParser("/sdcard/test.xml","GAMES");
		
		/**run the parser calling the method runParser()*/
		myXMLParser.runParser();
		
		/**I create a class ContentObjects that holds all my objects*/
		List<ContentObject> tempList;
		
		/**get the Parsed data from myXMLParser*/
		tempList=myXMLParser.getParsedData();
		
		Log.i(TAG,"Action objects are: "+tempList.size());
		Log.i(TAG,"myXMLParser has: "+myXMLParser.myContentObjects.size());

		/**iterate through the parsed objects contained in my XML using the Iterator class*/
		Iterator it = myXMLParser.myContentObjects.iterator();
		while(it.hasNext()) {
			ContentObject tempEmp = (ContentObject) it.next();
			Log.i(TAG,"This is an object");
			Log.i(TAG,"Name: "+tempEmp.getName());
			Log.i(TAG,"Image location: "+tempEmp.getLocationImage());
			Log.i(TAG,"Description: "+tempEmp.getDescription());
		}
	}
}

Ok, so one comment above says I create a class contentObjects that holds all my objects. I meant, I create an instance of.

Once I run the parser, the myXMLParser object contains all the information I need and I use myXMLParser.getParsedData() to pass it down to a list which I itterate through and display the information.

If you use this code, make sure to create the XmlSAXParser object in a method so that when the method is finished, the object is destroyed and you will not have it in memory somewhere doing nothing useful.

Well hope you understand all of this, if you dont, feel free to drop me a comment! Enjoy!

If you would like to display the results in a ListView, I suggest you look at my tutorials for JSON and/or SQLite

Alternative
I was asked, what if you need to grab the xml file from the res folder?

If you want it to put the xml into a project folder, you must change the implementation of the XmlSAXParser. The constructor in XMLSAXParser will be:

public XmlSAXParser(InputStream location, String selection){
mLocationStream = location;
mSelection = selection;
}

Then you need to change RunParser method:

public void runParser(){
     try{
          SAXParserFactory spf = SAXParserFactory.newInstance();
          SAXParser sp = spf.newSAXParser();
          XMLReader xr = sp.getXMLReader();
          xr.setContentHandler(this);
          xr.parse(new InputSource(mLocationStream));
     } catch (Exception e) {
     Log.i(TAG,"XML Pasing Excpetion = " + e);
     System.exit(-1);
     }
}

So that it takes an InputStream directly, not a FileInputStream that is created from the string path mLocation.

Finally, in TestActivity get the resource and give it to your constructor:

InputStream resource = this.getResources().openRawResource(R.raw.test);
XmlSAXParser myXMLParser = new XmlSAXParser(resource,"GAMES");


Comments are closed.