codeofaninja
website

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

Photo of Mike Dalisay
Modified Friday, February 15, 2013
by - @ninjazhai
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.

Parse XML in Android tutorial
Our code's logcat final output. Click to enlarge.

You can download our code here:

download code for Parse XML in Android tutorial

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;

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);
    }
}

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:
For FREE programming tutorials, click the red button below and subscribe! :)
Thanks for the comments!
 
 
Fundamentals
"First do it, then do it right, then do it better."
~ Addy Osmani
"Talk is cheap. Show me the code."
~ Linus Torvalds
Let's Stay Connected!
g+ r
Android app on Google Play
© 2011-2014 The Code Of A Ninja. All rights reserved. Proudly Powered by Google Blogger. Images, logos, marks or names mentioned herein are the property of their respective owners.