Android XML DOM parser with AsyncTask

22 November 2012 By Nithya Vasudevan 7,758 views One Comment
12 Flares Twitter 1 Facebook 1 Google+ 10 12 Flares ×

Project Description

  • My previous Android examples on DOM parser store the XML file in project’s assets folder and opens the file as InputStream using AssetManager.
  • This example stores the XML file in server and uses android.os.AsyncTask to download and parse the XML and display the result in ListView.

    Applications targeting the Honeycomb (Android 3) SDK or higher should perform networking IO operations using AsyncTask and not in main event loop threads. Trying to do in main thread causes android.os.NetworkOnMainThreadException.

  • When the list item is clicked a Toast message is displayed with description of XML element.

To parse an XML in Android, you can use DOM and SAX parser API provided by Java platform. In addition to these two parsers, Android provides XmlPullParser which is similar to StAX parser and also the recommended parser to be used in Android.

Environment Used

Prerequisites

Create Android Project

  • Create a new Android Project and name it as DOMParserAsyncTask.
  • Enter the package name as com.theopentutorials.android.
  • Enter the Activity name as DOMParserActivity.
  • Click Finish.

Create XML file

Create a new XML file and name it as “employees.xml” and copy the following. I placed this file in my <TOMCAT_HOME>/webapps/ROOT folder.

<?xml version="1.0" encoding="UTF-8"?>
<employees>
	<employee id = "2163">
	    <name>Kumar</name>
		<department>Development</department>
		<type>Permanent</type>		
		<email>[email protected]</email>
	</employee>
	
	<employee id = "6743">
		<name>Ram</name>
		<department>DB</department>
		<type>Contract</type>		
		<email>[email protected]</email>
	</employee>
	
	<employee id = "6763">
	    <name>Timmy</name>
		<department>Testing</department>
		<type>Permanent</type>		
		<email>[email protected]</email>
	</employee>
</employees>

strings.xml

Open res/values/string.xml and replace it with following content.

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="hello">Hello World, DOMParserActivity!</string>
    <string name="app_name">DOMParserAsyncTask</string>
    <string name="button">Parse XML using DOM</string>
</resources>

XML layout files

This Android application uses two layout files; one for displaying the main layout containing ListView to display the result of DOM parser and another for defining the layout for ListView item.
Open main.xml file in res/layout and copy the following content.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

     <Button
        android:id="@+id/button"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="@string/button" />
     
     <ListView
        android:id="@+id/employeeList"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" 
       />
</LinearLayout>

Create a new layout file list_item.xml in res/layout and copy the following content.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <TextView
        android:id="@+id/title"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:textColor="#CC0033"
        android:textSize="16sp"
        android:textStyle="bold" />

    <TextView
        android:id="@+id/department"
        android:layout_width="wrap_content"
        android:layout_height="fill_parent"
        android:textColor="#3399FF"
        android:textSize="12sp"
        android:textStyle="bold" />

    <TextView
        android:id="@+id/email"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="14sp"
        android:textStyle="italic"
        android:typeface="sans" />

</LinearLayout>

Create Bean classes

Employee.java

Create a new Java class “Employee.java” in package “com.theopentutorials.android.beans

package com.theopentutorials.android.beans;

public class Employee {

	private String name;
	private int id;
	private String department;
	private String type;
	private String email;
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getDepartment() {
		return department;
	}
	public void setDepartment(String department) {
		this.department = department;
	}
	public String getType() {
		return type;
	}
	public void setType(String type) {
		this.type = type;
	}
	public String getEmail() {
		return email;
	}
	public void setEmail(String email) {
		this.email = email;
	}
	@Override
	public String toString() {
		return "Employee [name=" + name + "]";
	}	
}

Create XMLDOMParser class

package com.theopentutorials.android.xml;

import java.io.IOException;
import java.io.InputStream;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import android.util.Log;

public class XMLDOMParser {
	// Returns the entire XML document
	public Document getDocument(InputStream inputStream) {
		Document document = null;
		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
		try {
			DocumentBuilder db = factory.newDocumentBuilder();
			InputSource inputSource = new InputSource(inputStream);
			document = db.parse(inputSource);
		} catch (ParserConfigurationException e) {
			Log.e("Error: ", e.getMessage(), e);
			return null;
		} catch (SAXException e) {
			Log.e("Error: ", e.getMessage(), e);
			return null;
		} catch (IOException e) {
			Log.e("Error: ", e.getMessage(), e);
			return null;
		}
		return document;
	}

	/*
	 * I take a XML element and the tag name, look for the tag and get the text
	 * content i.e for <employee><name>Kumar</name></employee> XML snippet if
	 * the Element points to employee node and tagName is name I will return
	 * Kumar. Calls the private method getTextNodeValue(node) which returns the
	 * text value, say in our example it is Kumar.
	 */
	public String getValue(Element item, String name) {
		NodeList nodes = item.getElementsByTagName(name);
		return this.getTextNodeValue(nodes.item(0));
	}

	private final String getTextNodeValue(Node node) {
		Node child;
		if (node != null) {
			if (node.hasChildNodes()) {
				child = node.getFirstChild();
				while (child != null) {
					if (child.getNodeType() == Node.TEXT_NODE) {
						return child.getNodeValue();
					}
					child = child.getNextSibling();
				}
			}
		}
		return "";
	}
}

Create Custom BaseAdapter class for ListView

package com.theopentutorials.android.adapters;

import java.util.List;
import com.theopentutorials.android.R;
import com.theopentutorials.android.beans.Employee;

import android.app.Activity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;

public class CustomListViewAdapter extends BaseAdapter {
	Activity context;
	List<Employee> employees;

	public CustomListViewAdapter(Activity context, 
				List<Employee> employees) {
		this.context = context;
		this.employees = employees;
	}

	// private View Holder class
	private class ViewHolder {
		TextView txtTitle;
		TextView txtDepartment;
		TextView txtEmail;
	}

	public int getCount() {
		return employees.size();
	}

	public Object getItem(int position) {
		return employees.get(position);
	}

	public long getItemId(int position) {
		return employees.indexOf(getItem(position));
	}

	public View getView(int position, View convertView, ViewGroup parent) {
		ViewHolder holder;
		LayoutInflater inflater = context.getLayoutInflater();

		if (convertView == null) {
			convertView = inflater.inflate(R.layout.list_item, null);
			holder = new ViewHolder();
			holder.txtTitle = (TextView) convertView.findViewById(R.id.title);
			holder.txtDepartment = (TextView) convertView
					.findViewById(R.id.department);
			holder.txtEmail = (TextView) convertView.findViewById(R.id.email);
			convertView.setTag(holder);
		} else {
			holder = (ViewHolder) convertView.getTag();
		}

		Employee employee = (Employee) getItem(position);

		holder.txtTitle.setText(employee.getId() + ": " + employee.getName());
		holder.txtDepartment.setText(employee.getDepartment() + ": "
				+ employee.getType());
		holder.txtEmail.setText("Email: " + employee.getEmail());

		return convertView;
	}
}

Create DOMParserActivity class

package com.theopentutorials.android;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.List;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import com.theopentutorials.android.adapters.CustomListViewAdapter;
import com.theopentutorials.android.beans.Employee;
import com.theopentutorials.android.xml.XMLDOMParser;

import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.Button;
import android.widget.ListView;
import android.widget.Toast;

public class DOMParserActivity extends Activity 
		implements OnClickListener,OnItemClickListener {
	ListView listView;
	Button button;
	List<Employee> employees;
	CustomListViewAdapter listViewAdapter;

	// All static variables
	static final String URL = "http://10.0.2.2:8080/employees.xml";

	// XML node names
	static final String ATTR_ID = "id";
	static final String NODE_EMP = "employee";
	static final String NODE_NAME = "name";
	static final String NODE_DEPT = "department";
	static final String NODE_TYPE = "type";
	static final String NODE_EMAIL = "email";
  
	/** Called when the activity is first created. */
	@Override  
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);

		findViewsById();
		button.setOnClickListener(this);
		listView.setOnItemClickListener(this);
	}

	private void findViewsById() {
		button = (Button) findViewById(R.id.button);
		listView = (ListView) findViewById(R.id.employeeList);
	}

	public void onClick(View view) {
		GetXMLTask task = new GetXMLTask(this);
		task.execute(new String[] { URL });
	}

	public void onItemClick(AdapterView<?> adapter, View view, int position,
			long id) {
		Toast.makeText(getApplicationContext(),
			employees.get(position).toString(), 
			Toast.LENGTH_SHORT).show();
	}

	//private inner class extending AsyncTask
	private class GetXMLTask extends AsyncTask<String, Void, String> {
		private Activity context;

		public GetXMLTask(Activity context) {
			this.context = context;
		}

		@Override
		protected String doInBackground(String... urls) {
			String xml = null;
			for (String url : urls) {
				xml = getXmlFromUrl(url);
			}
			return xml;
		}
		
		@Override
		protected void onPostExecute(String xml) {
			
			XMLDOMParser parser = new XMLDOMParser();
			InputStream stream = new ByteArrayInputStream(xml.getBytes());
			Document doc = parser.getDocument(stream);

			NodeList nodeList = doc.getElementsByTagName(NODE_EMP);

			employees = new ArrayList<Employee>();
			Employee employee = null;
			for (int i = 0; i < nodeList.getLength(); i++) {
				employee = new Employee();
				Element e = (Element) nodeList.item(i);
				employee.setId(Integer.parseInt(e.getAttribute(ATTR_ID)));
				employee.setName(parser.getValue(e, NODE_NAME));
				employee.setDepartment(parser.getValue(e, NODE_DEPT));
				employee.setType(parser.getValue(e, NODE_TYPE));
				employee.setEmail(parser.getValue(e, NODE_EMAIL));
				employees.add(employee);
			}

			listViewAdapter = new CustomListViewAdapter(context, employees);
			listView.setAdapter(listViewAdapter);
		}

		/* uses HttpURLConnection to make Http request from Android to download
		 the XML file */
		private String getXmlFromUrl(String urlString) {
			StringBuffer output = new StringBuffer("");

			InputStream stream = null;
			URL url;
			try {
				url = new URL(urlString);
				URLConnection connection = url.openConnection();

				HttpURLConnection httpConnection = (HttpURLConnection) connection;
				httpConnection.setRequestMethod("GET");
				httpConnection.connect();

				if (httpConnection.getResponseCode() == HttpURLConnection.HTTP_OK) {
					stream = httpConnection.getInputStream();
					BufferedReader buffer = new BufferedReader(
							new InputStreamReader(stream));
					String s = "";
					while ((s = buffer.readLine()) != null)
						output.append(s);
				}
			} catch (MalformedURLException e) {
				Log.e("Error", "Unable to parse URL", e);
			} catch (IOException e) {
				Log.e("Error", "IO Exception", e);
			}
					
			return output.toString();

			//For applications targeting Froyo and previous versions
			/*	
			String xml = null;
			try {
				DefaultHttpClient httpClient = new DefaultHttpClient();
				HttpGet httpGet = new HttpGet(url);

				HttpResponse httpResponse = httpClient.execute(httpGet);
				HttpEntity httpEntity = httpResponse.getEntity();
				xml = EntityUtils.toString(httpEntity);
			} catch (UnsupportedEncodingException e) {
				e.printStackTrace();
			} catch (ClientProtocolException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			}
			return xml;*/
		}	
	}
}

The above code works as follows;

  • Whenever we need to perform lengthy operation or any background operation we can use Asyntask which executes a task in background and publish results on the UI thread without having to manipulate threads and/or handlers.
  • In Button click event, we create and execute the task. This invokes doInBackground() where we open a Http URL connection and download the XML file, parse it using SAX and return list of laptops.
  • Once the background computation finishes, onPostExecute() is invoked on the UI thread which displays the result in ListView using custom ArrayAdapter or BaseAdapter.

We are running the remote server in the local machine itself (localhost). So you may try to access the resources using http://localhost:8080, but you may get “Connection refused” (failed to connect to localhost/127.0.0.1) exception because our code is running in an emulator so localhost refers to the emulator itself and not the machine which is running the Tomcat/Apache server. So to access local machine from emulator, you have to use 10.0.2.2 IP address which points to the machine which has the running emulator.

Android includes two HTTP clients: HttpURLConnection and Apache HTTP Client.
For Gingerbread and later, HttpURLConnection is the best choice.

AndroidManifest.xml

Define the activity in AndroidManifest.xml file. To access internet from Android application set the android.permission.INTERNET permission in manifest file as shown below.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.theopentutorials.android"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk android:minSdkVersion="15" />

    <uses-permission android:name="android.permission.INTERNET" />
    
    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name" >
        <activity
            android:name="com.theopentutorials.android.DOMParserActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>

Output

Run your application

Project Folder Structure

The complete folder structure of this example is shown below.

Tags: , , , , , , , , , , , , , , , , , , ,

  • azza

    thanks for the tutorial,but how can i display the result in map view