Android XML Parser Example with XML from SD Card, URL or Assets

XML parsing could be one of the most basic requirement on your Android application. In today’s tutorial, we are going to read XML files using three input sources. There can be three sources of your XML:

1. XML from device SD Card. Your dynamic XML file could be downloaded first for offline use.
2. XML from a URL. You could be parsing data from your online database or RSS feed. Works if device is online only.
3. XML from your app’s assets folder. Your XML file is not dynamic so it is better to put it in the asset’s folder where it cannot be change.

By the way, we’ll be using SAX parser here. I think that for mobile apps, SAX parser is better to use than DOM parser.

DOM parser consumes more memory because it loads the whole XML data to the device memory while SAX parser does an event driven approach. SAX parser processes each line of the XML without loading it to the device memory first.

xml-parsing-tutorial-output

DOWNLOAD SOURCE CODE HERE

Our XML structure looks like this:

<?xml version="1.0" encoding="utf-8" ?>
<Contents>
    <Owners>
        <Owner>
            <Name>Joselito Dimaculangan</Name>
            <Age>16</Age>
            <EmailAddress>joselito123@gmail.com</EmailAddress>
        </Owner>
        <Owner>
            <Name>Noemi De Galileo</Name>
            <Age>14</Age>
            <EmailAddress>noemi111@gmail.com</EmailAddress>
        </Owner>
    </Owners>
    <Dogs>
        <Dog>
            <Name>Barky</Name>
            <Birthday>June 29, 2012</Birthday>
        </Dog>
        <Dog>
            <Name>Jumbo</Name>
            <Birthday>December 30, 2012</Birthday>
        </Dog>
    </Dogs>
</Contents>

MainActivity.java code – Here you can change the value of x to 1,2 or 3 to change the input source. Read comments on code.

package com.example.androidparsexml;

import java.io.File;
import java.io.FileInputStream;
import java.net.URL;
import java.util.Iterator;
import java.util.List;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.InputSource;
import org.xml.sax.XMLReader;

import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.app.Activity;

public class MainActivity extends Activity {

    public static final String LOG_TAG = "MainActivity.java";

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        try {

            // parse our XML
            new parseXmlAsync().execute();

        } catch (NullPointerException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /*
     * @We are using an AsyncTask to avoid
     * android.os.NetworkOnMainThreadException when parsing from a URL
     *
     * @If you don't know a thing about AsyncTasks,there are a lot of excellent
     * tutorial out there, see this thread
     */
    private class parseXmlAsync extends AsyncTask<String, String, String> {

        @Override
        protected String doInBackground(String... strings) {

            try {

                /*
                 * You may change the value of x to try different sources of XML
                 *
                 * @1 = XML from SD Card
                 *
                 * @2 = XML from URL
                 *
                 * @3 = XML from assets folder
                 */
                int x = 2;

                // initialize our input source variable
                InputSource inputSource = null;

                // XML from sdcard
                if (x == 1) {

                    // make sure sample.xml is in your root SD card directory
                    File xmlFile = new File(
                            Environment.getExternalStorageDirectory()
                                    + "/sample.xml");
                    FileInputStream xmlFileInputStream = new FileInputStream(
                            xmlFile);
                    inputSource = new InputSource(xmlFileInputStream);
                }

                // XML from URL
                else if (x == 2) {
                    // specify a URL
                    // make sure you are connected to the internet
                    URL url = new URL(
                            "http://demo.codeofaninja.com/AndroidXml/sample.xml");
                    inputSource = new InputSource(url.openStream());
                }

                // XML from assets folder
                else if (x == 3) {
                    inputSource = new InputSource(getAssets()
                            .open("sample.xml"));
                }

                // instantiate SAX parser
                SAXParserFactory saxParserFactory = SAXParserFactory
                        .newInstance();
                SAXParser saxParser = saxParserFactory.newSAXParser();

                // get the XML reader
                XMLReader xmlReader = saxParser.getXMLReader();

                // prepare and set the XML content or data handler before
                // parsing
                XmlContentHandler xmlContentHandler = new XmlContentHandler();
                xmlReader.setContentHandler(xmlContentHandler);

                // parse the XML input source
                xmlReader.parse(inputSource);

                // put the parsed data to a List
                List<ParsedDataSet> parsedDataSet = xmlContentHandler
                        .getParsedData();

                // we'll use an iterator so we can loop through the data
                Iterator<ParsedDataSet> i = parsedDataSet.iterator();
                ParsedDataSet dataItem;

                while (i.hasNext()) {

                    dataItem = (ParsedDataSet) i.next();

                    /*
                     * parentTag can also represent the main type of data, in
                     * our example, "Owners" and "Dogs"
                     */
                    String parentTag = dataItem.getParentTag();
                    Log.v(LOG_TAG, "parentTag: " + parentTag);

                    if (parentTag.equals("Owners")) {
                        Log.v(LOG_TAG, "Name: " + dataItem.getName());
                        Log.v(LOG_TAG, "Age: " + dataItem.getAge());
                        Log.v(LOG_TAG,
                                "EmailAddress: " + dataItem.getEmailAddress());
                    }

                    else if (parentTag.equals("Dogs")) {
                        Log.v(LOG_TAG, "Name: " + dataItem.getName());
                        Log.v(LOG_TAG, "Birthday: " + dataItem.getBirthday());
                    }

                }

            } catch (NullPointerException e) {
                e.printStackTrace();
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }

        @Override
        protected void onPostExecute(String lenghtOfFile) {
            // your do stuff after parsing the XML
        }
    }

}

XmlContentHandler.java – We’ll extend the default handler.

package com.example.androidparsexml;

import java.util.ArrayList;
import java.util.List;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

import android.util.Log;

public class XmlContentHandler extends DefaultHandler {

    private static final String LOG_TAG = "XmlContentHandler";

    // used to track of what tags are we
    private boolean inOwner = false;
    private boolean inDog = false;

    // accumulate the values
    private StringBuilder mStringBuilder = new StringBuilder();

    // new object
    private ParsedDataSet mParsedDataSet = new ParsedDataSet();

    // the list of data
    private List<ParsedDataSet> mParsedDataSetList = new ArrayList<ParsedDataSet>();

    /*
     * Called when parsed data is requested.
     */
    public List<ParsedDataSet> getParsedData() {
        Log.v(LOG_TAG, "Returning mParsedDataSetList");
        return this.mParsedDataSetList;
    }

    // Methods below are built in, we just have to do the tweaks.

    /*
     * @Receive notification of the start of an element.
     *
     * @Called in opening tags such as <Owner>
     */
    @Override
    public void startElement(String namespaceURI, String localName,
            String qName, Attributes atts) throws SAXException {

        if (localName.equals("Owner")) {
            // meaning new data object will be made
            this.mParsedDataSet = new ParsedDataSet();
            this.inOwner = true;
        }

        else if (localName.equals("Dog")) {
            this.mParsedDataSet = new ParsedDataSet();
            this.inDog = true;
        }

    }

    /*
     * @Receive notification of the end of an element.
     *
     * @Called in end tags such as </Owner>
     */
    @Override
    public void endElement(String namespaceURI, String localName, String qName)
            throws SAXException {

        // Owners
        if (this.inOwner == true && localName.equals("Owner")) {
            this.mParsedDataSetList.add(mParsedDataSet);
            mParsedDataSet.setParentTag("Owners");
            this.inOwner = false;
        }

        else if (this.inOwner == true && localName.equals("Name")) {
            mParsedDataSet.setName(mStringBuilder.toString().trim());
        }

        else if (this.inOwner == true && localName.equals("Age")) {
            mParsedDataSet.setAge(mStringBuilder.toString().trim());
        }

        else if (this.inOwner == true && localName.equals("EmailAddress")) {
            mParsedDataSet.setEmailAddress(mStringBuilder.toString().trim());
        }

        // Dogs
        if (this.inDog == true && localName.equals("Dog")) {
            this.mParsedDataSetList.add(mParsedDataSet);
            mParsedDataSet.setParentTag("Dogs");
            this.inDog = false;
        }

        else if (this.inDog == true && localName.equals("Name")) {
            mParsedDataSet.setName(mStringBuilder.toString().trim());
        }

        else if (this.inDog == true && localName.equals("Birthday")) {
            mParsedDataSet.setBirthday(mStringBuilder.toString().trim());
        }

        // empty our string builder
        mStringBuilder.setLength(0);
    }

    /*
     * @Receive notification of character data inside an element.
     *
     * @Gets be called on the following structure: <tag>characters</tag>
     */
    @Override
    public void characters(char ch[], int start, int length) {
        // append the value to our string builder
        mStringBuilder.append(ch, start, length);
    }
}

ParsedDataSet.java – You can use your own object class if the XML you’re parsing is for a more specific data object. For example, you are parsing only for “Owners” and NOT “Owners & Dogs” like what we have.

package com.example.androidparsexml;

public class ParsedDataSet {

	private String parentTag = null;
	private String name = null;
	private String age = null;
	private String emailAddress = null;
	private String birthday = null;

	// parent tag
	public String getParentTag() {
		return parentTag;
	}

	public void setParentTag(String parentTag) {
		this.parentTag = parentTag;
	}

	// name
	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	// age
	public String getAge() {
		return age;
	}

	public void setAge(String age) {
		this.age = age;
	}

	// emailAddress
	public String getEmailAddress() {
		return emailAddress;
	}

	public void setEmailAddress(String emailAddress) {
		this.emailAddress = emailAddress;
	}

	// birthday
	public String getBirthday() {
		return birthday;
	}

	public void setBirthday(String name) {
		this.birthday = name;
	}
}

Our AndroidManifest.xml will have internet permissions because we use a URL input

<uses-permission android:name="android.permission.INTERNET" />

Our code output on the device look simply like this:

Device output.

The Code of a Ninja Resources

  • Ollie Strevel

    You repeated the XmlContentHandler’s code where it should be ParsedDataSet’s code.
    So, the faster XML parser is SAX?

    • ninjazhai

      Hey @Ollie, yup, please consider this answer too http://stackoverflow.com/a/15608688/827418

      • http://jpmurray.net Jean-Philippe Murray

        You still have twice the same code in your post…

        • ninjazhai

          Oh I’m sorry, I see it now… fixed!