Android Core

Boost your Android XML parsing with XML Pull

In today’s technology world, XML parsing capabilities are a necessity for a big number of applications whether they are mobile, enterprise or in the cloud. Despite the fact that XML is bloated and very verbose, it still remains king in the field of data exchange (JSON is a nice alternative, check our tutorial for JSON integration with GWT).

There are two main approaches in XML parsing: SAX and DOM. The SAX specification defines an event-based approach where the implemented parsers scan through XML data and they use call-back handlers whenever certain parts of the document have been reached. On the other hand, the DOM specification defines a tree-based approach to navigating an XML document.

In a previous tutorial we saw how to parse XML documents using SAX. DOM object is relatively resources intensive and perhaps not suitable for use in a mobile environment. A SAX parser is quite more lightweight and uses a smaller memory footprint. SAX is a push parsing API but the approach it uses is somewhat “broken” in the sense that, rather than being called by the parsing application, the SAXParser uses a message handler with “call backs”.

An alternative to that is using a relatively new practice, the “pull parsing” approach. In short, the main difference with this approach is that the user code is in control and can pull more data when it is ready to process it. You can find an excellent article in processing XML with the XML Pull Parser as well as some XML pull parsing patterns.

The Android SDK includes support for XML Pull parsing (which surprisingly is there from Level 1 API) via the XML Pull package. The main class used is the XmlPullParser with the Javadoc page including a simple example of how to use the parser. In this tutorial I am going to show you how to add pull parsing capabilities into your Android application and how to implement a more sophisticated parser than the one provided by the API docs.

If you are a regular JavaCodeGeeks reader, you probably know that I have started a tutorial series where I am building a full application from scratch. In its third part (“Android Full App, Part 3: Parsing the XML response”), I use an XML based external API in order to perform movies search. A sample XML response is the following:

Movies search for “Transformers” and (year) “2007”

In that tutorial I presented the SAX based approach, but know we are going to boost things up by using Android’s XML pull parser. First, let’s create a new Android project inside Eclipse. I am calling it “AndroidXmlPullParserProject”. Here is a screenshot of the configuration used:

The first step in using the XML Pull API is to obtain a new instance of the XmlPullParserFactory class. This class is used to create implementations of XML Pull Parser defined in XMPULL V1 API. We will disable the namespace awareness of the factory since it is not required by the application’s needs. Note that this will also improve the parsing speed.

Next, we create a new XmlPullParser by invoking the newPullParser factory method. An input has to be provided to our parser and that is accomplished via the setInput method, which requires an InputStream and an encoding as arguments. We provide an input stream obtained by a URL connection (since our XML document is an internet resource), but we do not provide an input encoding (null is just fine).

XML Pull parsing is event-based and in order to parse the whole document, the trick is to create a loop inside which we serially get all the parsing events until we reach the END_DOCUMENT event. As a showcase example, the code will just print log statements when the following events are encountered:

Here is the source code for our first simple implementation:

package com.javacodegeeks.android.xml.pull;

import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserFactory;

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

public class XmlPullParserActivity extends Activity {
    
    private static final String xmlUrl = 
        "http://dl.dropbox.com/u/7215751/JavaCodeGeeks/AndroidFullAppTutorialPart03/Transformers+2007.xml";
    
    private final String TAG = getClass().getSimpleName();
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
    
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);                

        try {
            parseFromUrl();
        } 
        catch (Exception e) {
            Log.e(TAG, "Error while parsing", e);
        }
        
    }
    
    private void parseFromUrl() throws Exception {
        
        XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
        factory.setNamespaceAware(false);
        XmlPullParser xpp = factory.newPullParser();
        
        URL url = new URL(xmlUrl);
        URLConnection ucon = url.openConnection();
        InputStream is = ucon.getInputStream();
        
        xpp.setInput(is, null);
        
        int eventType = xpp.getEventType();
        
        while (eventType != XmlPullParser.END_DOCUMENT) {
            
            if (eventType == XmlPullParser.START_DOCUMENT) {
                Log.d(TAG, "Start document");
            }
            else if (eventType == XmlPullParser.END_DOCUMENT) {
                Log.d(TAG, "End document");
            }
            else if (eventType == XmlPullParser.START_TAG) {
                Log.d(TAG, "Start tag " + xpp.getName());
            }
            else if (eventType == XmlPullParser.END_TAG) {
                Log.d(TAG, "End tag " + xpp.getName());
            }
            else if (eventType == XmlPullParser.TEXT) {
                Log.d(TAG, "Text " + xpp.getText());
            }
            
            eventType = xpp.next();
            
        }
        
    }

Include the INTERNET permission into your Android Manifest file and launch the project. Go to the DDMS view of Eclipse and create a new filter using the class name “XmlPullParserActivity” as shown in the following image:

You should then find the various log messages in the LogCat view:

Notice that no special parsing has occurred. We just got notified when the parser found a new tag, reached the document end etc. However, since we are sure that we have the basic infrastructure ready, we can step it up a bit. First, take a look at the sample XML (provided by the TMDb API):

It is your typical XML document with nested elements etc. The data that are interesting to us are those inside the “movies” element. We will create a Movie class and map each child element to the corresponding class field. Moreover, we will also create an Image class using the same approach. Note that a Movie can have zero or more Images. Thus, the two domain model classes are:

package com.javacodegeeks.android.xml.pull.model;

import java.util.ArrayList;

public class Movie {
    
    public String score;
    public String popularity;
    public boolean translated;
    public boolean adult;
    public String language;
    public String originalName;
    public String name;
    public String type;
    public String id;
    public String imdbId;
    public String url;
    public String votes;
    public String rating;
    public String certification;
    public String overview;
    public String released;
    public String version;
    public String lastModifiedAt;
    public ArrayList<Image> imagesList;

}
package com.javacodegeeks.android.xml.pull.model;

public class Image {
    
    public String type;
    public String url;
    public String size;
    public int width;
    public int height;

}

We are now ready to start the parsing. We first create the factory and the pull parser the same way as before. Note that the document does not directly start with the “movies” element, but there are a few elements that we wish to skip. That is accomplished by using the methods nextTag (for START_TAG and END_TAG events) and nextText (for TEXT events).

Now we are ready to proceed with the interesting parsing. We are going to use a “recursion-like” approach. The “movies” element contains a number of “movie” elements where a “movie” element contains a number of “image” elements. Thus, we “drill-down” from the parent elements to the child ones using a dedicated method for the parsing of each element. From one method to an other, we pass the XmlPullParser instance as argument since there is a unique parser implementing the parsing. The result of each method is an instance of the model class and finally a list of movies. In order to check the name of the current element, we use the getName method and in order to retrieve the enclosed text we use the nextText method. For attributes, we use the getAttributeValue method where the first argument is the namespace (null in our case) and the second is the attribute name.

Enough talking, let’s see how all these are translated to code:

package com.javacodegeeks.android.xml.pull;

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;

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

import com.javacodegeeks.android.xml.pull.model.Image;
import com.javacodegeeks.android.xml.pull.model.Movie;

public class XmlPullParserActivity extends Activity {
    
    private static final String xmlUrl = 
        "http://dl.dropbox.com/u/7215751/JavaCodeGeeks/AndroidFullAppTutorialPart03/Transformers+2007.xml";
    
    private final String TAG = getClass().getSimpleName();
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
        
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);                

        try {
            List<Movie> movies = parseFromUrl();
            for (Movie movie : movies) {
                Log.d(TAG, "Movie:"+movie);
            }
        } 
        catch (Exception e) {
            Log.e(TAG, "Error while parsing", e);
        }
        
    }
    
    private List<Movie> parseFromUrl() throws XmlPullParserException, IOException {
        
        List<Movie> moviesList = null;
        
        XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
        factory.setNamespaceAware(false);
        XmlPullParser parser = factory.newPullParser();
        
        URL url = new URL(xmlUrl);
        URLConnection ucon = url.openConnection();
        InputStream is = ucon.getInputStream();
        
        parser.setInput(is, null);
        
        parser.nextTag();        
        parser.nextTag();        
        parser.nextTag();        
        parser.nextTag();        
        parser.nextText();        
        parser.nextTag();
                
        moviesList = parseMovies(parser);
        
        return moviesList;
        
    }
    
    private List<Movie> parseMovies(XmlPullParser parser) throws XmlPullParserException, IOException {
        
        List<Movie> moviesList = new LinkedList<Movie>();
        
        Log.d(TAG, "parseMovies tag " + parser.getName());

        while (parser.nextTag() == XmlPullParser.START_TAG) {
            Log.d(TAG, "parsing movie");
            Movie movie = parseMovie(parser);
            moviesList.add(movie);
        }
        
        return moviesList;
        
    }
    
    private Movie parseMovie(XmlPullParser parser) throws XmlPullParserException, IOException {
        
        Movie movie = new Movie();
        Log.d(TAG, "parseMovie tag " + parser.getName());
        
        while (parser.nextTag() == XmlPullParser.START_TAG) {
            
            if (parser.getName().equals("name")) {
                movie.name = parser.nextText();
            } 
            else if (parser.getName().equals("score")) {
                movie.score = parser.nextText();
            }
            else if (parser.getName().equals("images")) {
                Image image = parseImage(parser);
                movie.imagesList = new ArrayList<Image>();
                movie.imagesList.add(image);
            }
            else if (parser.getName().equals("version")) {
                movie.version = parser.nextText();
            }
            else {
                parser.nextText();
            }
            
        }
        
        return movie;
        
    }
    
    private Image parseImage(XmlPullParser parser) throws XmlPullParserException, IOException {
        
        Image image = new Image();
        Log.d(TAG, "parseImage tag " + parser.getName());
        
        while (parser.nextTag() == XmlPullParser.START_TAG) {
            
            if (parser.getName().equals("image")) {
                image.type = parser.getAttributeValue(null, "type");
                image.url = parser.getAttributeValue(null, "url");
                image.size = parser.getAttributeValue(null, "size");
                image.width = Integer.parseInt(parser.getAttributeValue(null, "width"));
                image.height = Integer.parseInt(parser.getAttributeValue(null, "height"));
            }
            parser.next();
            
        }
        
        return image;
        
    }
    
}

The code is pretty straightforward, just remember that we are using a “drill-down” approach in order to parse the deeper element (Movies ? Movie ? Images). Please note that in the Movie parsing method we included only some of the fields for brevity reasons. Also, do not forget to invoke the parser.nextText() method in order to allow the parser to move and fetch the next tag (else you will get some nasty exceptions since the current event will not be of type START_TAG).

Run the project’s configuration again and check that the LogCat contains the correct debugging statements:

That’s it! XML Pull parsing capabilities straight to your Android application. You can download the Eclipse project created for this article here.

Related Articles :

Ilias Tsagklis

Ilias is a software developer turned online entrepreneur. He is co-founder and Executive Editor at Java Code Geeks.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

2 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
BOUARE
BOUARE
11 years ago

please i need your main file xml please i don’t know how to see my result without the contain of your main file
thanks !!

abeer qamer
7 years ago

I would say icoderslab, it is a great place to get started they have a lot of programming tutorials and they’re all written in a coherent manner which are easily understandable.

check this out:

http://www.icoderslab.com/android-xml-parsing-in-android-studio-using-dom/?preview_id=512&preview_nonce=f428a0c359&_thumbnail_id=838&preview=true

Back to top button