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.
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:
6 responses to “Android XML Parser Example with XML from SD Card, URL or Assets”
You repeated the XmlContentHandler’s code where it should be ParsedDataSet’s code.
So, the faster XML parser is SAX?
Hey @Ollie, yup, please consider this answer too http://stackoverflow.com/a/15608688/827418
You still have twice the same code in your post…
Oh I’m sorry, I see it now… fixed!
Hi, When i tried this code Log.v(LOG_TAG, “Name: ” + dataItem.getName()); gives me the right name value on Log. But when i tried if( dataItem.getName()==”John”) name value seems like {java.lang.String@1234}”John” on wathc and if statement gives me false. can you help with that.
Hi Mike Dalisay, you cant post a sample with loop disable? I want to disable loop i dont have multiples “Owner”.