Android App Development: Parsing Web Service Response part 1

By
On June 15, 2011

In a previous last post we saw how to call REST and SOAP web services. The web service reponse can be one of the following:

  1. XML.
  2. SOAP.
  3. JSON.

Parsing XML

Android offers three types of XML parsers:

  1. DOM Parser.
  2. Pull Parser.
  3. SAX Parser.

we’ll demonstrate each using the following xml example:

<?xml version="1.0"?>
<person>
    <firstname>Jack</firstname>
    <lastname>smith</lastname>
    <age>28</age>
</person>

which we need to parse to create an object from Person class:

public class Person{
    	public String firstName;
    	public String lastName;
    	public int age;
    }

Parsing the response with DOM Parser:

Android provides org.w3c.dom library that contains classes used to parse xml by constructing a document and
matching each node to parse the info.
to parse our example response with DOM parser, we implement a function like this

void parseByDOM(String response) throws ParserConfigurationException, SAXException, IOException{
    	Person person=new Person();
    	DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
    	DocumentBuilder db = dbf.newDocumentBuilder();
    	Document doc = db.parse(new InputSource(new StringReader(response)));
    	// normalize the document
    	doc.getDocumentElement().normalize();
    	// get the root node
    	NodeList nodeList = doc.getElementsByTagName("person");
    	Node node=nodeList.item(0);
    	// the  node has three child nodes
    	for (int i = 0; i < node.getChildNodes().getLength(); i++) {
    	Node temp=node.getChildNodes().item(i);
    	if(temp.getNodeName().equalsIgnoreCase("firstname")){
    		person.firstName=temp.getTextContent();
    	}
    	else if(temp.getNodeName().equalsIgnoreCase("lastname")){
    		person.lastName=temp.getTextContent();
    	}
    	else if(temp.getNodeName().equalsIgnoreCase("age")){
    		person.age=Integer.parseInt(temp.getTextContent());
    	}

    	}

    	Log.e("person", person.firstName+ " "+person.lastName+" "+String.valueOf(person.age));
    }

The previous method is good, it retrieves the info correctly, but it requires that you are familiar with the xml structure so that you know the order of each xml node.
luckily Android provides a better approach of parsing using SAX parser.

Parsing the response with SAX Parser:

Android provides org.xml.sax package that has that provides the event-driven SAX parser.
to parse the previous response with SAX parser, we have to create a class extending DefaultHandler and override the following methods:

  1. startDocument(): invoked when the xml document is open, there we can initialize any member variables.
  2. startElement(String uri, String localName, String qName, Attributes attributes): invoked when the parser encounters a xml node, here we can initialize specific instances of our person object.
  3. endElement(String uri, String localName, String Name): invoked when the parser reaches the closing of a xml tag. here the element value would have been completely read.
  4. characters(char[] ch, int start, int length): this method is called when the parser reads characters of a node value.

so our parsing class will be like this:

/*
	 * SAX parser to parse persons response
	 */
	public class PersonParser extends DefaultHandler
	{

		// arraylist to store person objects
		ArrayList persons;
		// temporary person object
		Person tempPerson;
		// string builder acts as a buffer
		StringBuilder builder;

		/**
		 * Initialize the arraylist
		 * @throws SAXException
		 */
		@Override
		public void startDocument() throws SAXException {
			pesons=new ArrayList();

		}

		/**
		 * Initialize the temp person object which will hold the parsed info
		 * and the string builder that will store the read characters
		 * @param uri
		 * @param localName
		 * @param qName
		 * @param attributes
		 * @throws SAXException
		 */
		@Override
		public void startElement(String uri, String localName, String qName,
				Attributes attributes) throws SAXException {

			if(localName.equalsIgnoreCase.equals("person")){
				tempPerson=new Person();
				builder=new StringBuilder();
			}

		}
		/**
		 * Finished reading the person tag, add it to arraylist
		 * @param uri
		 * @param localName
		 * @param qName
		 * @throws SAXException
		 */
		@Override
		public void endElement(String uri, String localName, String qName)
				throws SAXException {
			// finished reading a person, add it to the arraylist
			if(localName.toLowerCase().equals("person"))
			{
				this.persons.add(tempPerson);
			}
			// finished reading "firstname" tag assign it to the temp person
			else if(localName.toLowerCase().equals("firstname")){
				tempPerson.firstName=builder.toString();
			}
			// finished reading "lastname" tag assign it to the temp person
			else if(localName.toLowerCase().equals("lastname")){
				tempPerson.lastName=builder.toString();
			}
			// finished reading "age" tag assign it to the temp person
			else if(localName.toLowerCase().equals("age")){
				tempPerson.age=Integer.parseInt(builder.toString());
			}
		}

		/**
		 * Read the value of each tag
		 * @param ch
		 * @param start
		 * @param length
		 * @throws SAXException
		 */
		@Override
		public void characters(char[] ch, int start, int length)
				throws SAXException {
			// read the characters and append them to the buffer
			String tempString=new String(ch, start, length);
			 builder.append(tempString);
		}
	}

the code is pretty easy, the parser iterates over each node, you check the current node name and take an action.

then we call the parser like this:

 public ArrayList getPersons(final String response) throws ParserConfigurationException, SAXException, IOException
	{
		BufferedReader br=new BufferedReader(new StringReader(response));
		InputSource is=new InputSource(br);
		PersonParser parser=new PersonParser();
		SAXParserFactory factory=SAXParserFactory.newInstance();
   		SAXParser sp=factory.newSAXParser();
   		XMLReader reader=sp.getXMLReader();
   		reader.setContentHandler(parser);
   		reader.parse(is);
   		ArrayList persons=parser.persons;

		return persons;

	}

Parsing JSON respone:

what if our repspone was JSON instead of xml. it would be something like this:

"persons"
[
{
"person"{
"firstName": "John",
     "lastName": "Smith",
     "age": 25
}
}
{
"person"{
"firstName": "Catherine",
     "lastName": "Jones",
     "age": 35
}
}
]

this response is a JSON Array with the name “persons”, this array consists of “person” JSON Objects.
to parse such a reponse:

public ArrayList<Person> getMessage(String response){
		JSONObject jsonResponse;
		ArrayList<Person> arrPersons=new ArrayList<Person>;
		try {
			// obtain the reponse
			jsonResponse = new JSONObject(response);
			// get the array
			JSONArray persons=jsonResponse.optJSONArray("persons");
			// iterate over the array and retrieve single person instances
			for(int i=0;i<persons.length();i++){
				// get person object
				JSONObject person=persons.getJSONObject(i);
				// get first name
				String firstname=person.optString("firstname");
				// get last name
				String lastname=person.optString("lastname");
				// get the age
				int age=person.optInt("age");

				// construct the object and add it to the arraylist
				Person p=new Person();
				p.firstName=firstname;
				p.lastName=lastname;
				p.age=age;
				arrPersons.add(p);
			}

		} catch (JSONException e) {

			e.printStackTrace();
		}

		return arrPersons;
	}

much easier than the previous methods.
notice that we used the methods optJSONArray,optString,optInt instead of using getString,getInt because the opt methods return empty strings or zero integers if no elements are found. while the get methods throw an exception if the element is not found.

and that was all about parsing web service reponses, stay tuned next week for another tutorial