Consuming WCF SVC XML Web Service with Attributes in Android


The Roadside Beauty Salon

In today’s ingeniously connected world, no Android app is complete without internet access. Being a mobile app consultant at Truiton, sometimes I feel, I keep doing stuff that no one else has done ever before, for e.g. take topic of this tutorial Consuming WCF SVC XML Web Service with Attributes in Android. I searched internet for hours, hoping to find some info about how to invoke a DotNet based WCF SOAP web service with attributes in XML, but found nothing. Eventually I figured out a way and thought to share it with the community by means of this Consuming WCF SVC XML Web Service with Attributes in Android tutorial.

While searching for ways on how to consume WCF SOAP web service, I found an open source project for Android named Ksoap2. This comes in a form of .jar file which can be included in any Android app project, this is a lightweight and efficient SOAP client library, at least this is what it’s page claims. I found various tutorials explaining on how to consume XML soap web services but none of them explains how to send attributes with input request data. In fact, I looked up the wiki for ksoap2, hoping to find a way to add attributes to request XML, but found nothing. While searching for an answer, I started to think why should I include a third party library for such a small task of consuming a WCF SVC XML web service with attributes in Android. Hence I dropped searching in that direction.

Finally I found another way through which I could invoke a WCF SVC XML web service with attributes in Android, i.e. via HttpClient, HttpPost, HttpResponse, and HttpEntity classes of org.apache.http package. This way we can create a simple http request and send it to the desired url. Now since we are not using Ksoap2 library, there is still a question to be answered i.e. how will we parse the response? Answer to that is simple XML SAX parser, with document builder class. The main advantage of this approach is that we can send and parse attributes in request and response respectively. Lets have a look at my request for Soap xml web service:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tem="http://tempuri.org/">
   <soapenv:Header/>
   <soapenv:Body>
      <tem:Login>
         <!--Optional:-->
         <tem:XInputXML>
            <!--You may enter ANY elements at this point-->
            <CREDENTIALS OF_OFFICEID="TRUITON" USER_PASSWORD="nilnil" EMAIL_ID="[email protected]"/>
         </tem:XInputXML>
      </tem:Login>
   </soapenv:Body>
</soapenv:Envelope>

Since we are not using any library to consume DotNet WCF soap web service with attributes in request, you need to create this request manually, which is quite simple to do. Have a look at the request above, in this request we are invoking Login method of SVC XML web service on Andorid. Here have a look at the highlighted part, i.e. the CREDENTIALS tag, it has three attributes. One of the main reasons we are using this HttpPost class is because of the attributes.

Lets have a look at the class which is consuming WCF SVC XML web service with attributes in Android:

package com.truiton.wcfconnect;

import java.io.StringReader;
import java.util.HashMap;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.protocol.HTTP;
import org.apache.http.util.EntityUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;

import android.os.AsyncTask;
import android.os.Bundle;
import android.app.Activity;
import android.util.Log;
import android.view.Menu;

public class TruitonMainActivity extends Activity {
 String LOG_TAG = "TruitonMainActivity";
 private UserLoginTask mAuthTask = null;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_truiton_main);
 mAuthTask = new UserLoginTask();
 mAuthTask.execute((Void) null);
 }

 @Override
 public boolean onCreateOptionsMenu(Menu menu) {
 // Inflate the menu; this adds items to the action bar if it is present.
 getMenuInflater().inflate(R.menu.activity_truiton_main, menu);
 return true;
 }

 void doWhateverYouWant(){
 // TODO: Write here the code which needs to be executed after AsyncTask.
 }

 public class UserLoginTask extends AsyncTask<Void, Void, Boolean> {
 @Override
 protected Boolean doInBackground(Void... params) {
 // TODO: attempt authentication against a network service.

 String SOAP_ACTION = "http://tempuri.org/MyService/Login";
 String URL = getString(R.string.URL);
 //String URL = "http:// *your URL here*";
 String response = null;
 try {
 HttpClient httpClient = new DefaultHttpClient();
 HttpPost httpPost = new HttpPost(URL);
 String bodyOut = "<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:tem=\"http://tempuri.org/\">"
 + "   <soapenv:Header/>"
 + "   <soapenv:Body>"
 + "      <tem:Login>"
 + "         <!--Optional:-->"
 + "         <tem:XInputXML>"
 + "            <!--You may enter ANY elements at this point-->"
 + "            <CREDENTIALS OF_OFFICEID=\"TRUITON\" USER_PASSWORD=\"nilnil\" EMAIL_ID=\"[email protected]\"/>"
 + "         </tem:XInputXML>"
 + "      </tem:Login>"
 + "   </soapenv:Body>" + "</soapenv:Envelope>";

 StringEntity se = new StringEntity(bodyOut, HTTP.UTF_8);
 se.setContentType("text/xml");
 httpPost.addHeader("SOAPAction", SOAP_ACTION);
 httpPost.setEntity(se);

 HttpResponse httpResponse = httpClient.execute(httpPost);
 HttpEntity resEntity = httpResponse.getEntity();
 response = EntityUtils.toString(resEntity);
 DocumentBuilderFactory factory = DocumentBuilderFactory
 .newInstance();
 DocumentBuilder builder = factory.newDocumentBuilder();
 StringReader sr = new StringReader(response);
 InputSource is = new InputSource(sr);
 Document XMLResponse = builder.parse(is);
 NodeList elements = XMLResponse.getElementsByTagName("ERRORS");
 Boolean error = Boolean.valueOf(elements.item(0)
 .getAttributes().item(0).getNodeValue());
 HashMap<String, String> Data = new HashMap<String, String>();
 if (error) { // case of no error, value of error is returned
 // true in case of no error.
 elements = XMLResponse
 .getElementsByTagName(
 "AUTH_OUTPUT").item(0)
 .getChildNodes();

 for (int index = 0; index < elements.getLength(); index++) {
 String elemName = elements.item(index).getNodeName();
 if (elements.item(index).hasAttributes()) { // if has
 // Attributes
 int attLength = elements.item(index)
 .getAttributes().getLength();
 for (int attIndex = 0; attIndex < attLength; attIndex++) {
 String attName = elements.item(index)
 .getAttributes().item(attIndex)
 .getNodeName();
 String attValue = elements.item(index)
 .getAttributes().item(attIndex)
 .getNodeValue();
 Data.put(elemName + "_" + attName, attValue);
 }
 } else { // in case of no attributes
 Data.put(elemName, elements.item(index)
 .getTextContent());
 }
 }
 elements = XMLResponse.getElementsByTagName("METRO_CITY")
 .item(0).getChildNodes();
 String elemName = "METRO_CITY";
 int childNo = elements.getLength();
 for (int chIndex = 0; chIndex < childNo; chIndex++) {
 Node childNode = elements.item(chIndex);
 String chName = childNode.getNodeName();
 if (childNode.hasAttributes()) { // if has attributes
 int attLength = childNode.getAttributes()
 .getLength();
 for (int attIndex = 0; attIndex < attLength; attIndex++) {
 String attName = childNode.getAttributes()
 .item(attIndex).getNodeName();
 String attValue = childNode.getAttributes()
 .item(attIndex).getNodeValue();
 String val = Data.get(elemName + "_" + chName
 + "_" + attName);
 if (val == null) {
 Data.put(elemName + "_" + chName + "_"
 + attName, attValue);
 } else {
 Data.put(elemName + "_" + chName + "_"
 + attName, val + "," + attValue);
 }
 }
 }
 }
 } else {// In case of error
 String eCode = elements.item(0).getChildNodes().item(0)
 .getAttributes().item(0).getNodeValue();
 String eDesc = elements.item(0).getChildNodes().item(0)
 .getAttributes().item(1).getNodeValue();
 Exception e = new Exception("Server Response: Error Code- "
 + eCode + " -" + eDesc);
 throw e;
 }
 } catch (Exception e) {
 Log.e(LOG_TAG, e.getMessage());
 } finally {
 Log.v(LOG_TAG + " Response", response);
 }
 return true;
 }

 @Override
 protected void onPostExecute(final Boolean success) {
 mAuthTask = null;

 if (success) {
 Log.v(LOG_TAG + " AsyncTask", "Success");
 doWhateverYouWant();
 } else {
 Log.e(LOG_TAG + " AsyncTask", "Fail");
 }
 }

 @Override
 protected void onCancelled() {
 mAuthTask = null;
 }
 }
}

Since the release of Honeycomb we cannot use network on main thread, therefore I used AsyncTask to call the WCF SVC XML web service with attributes on Android. This tutorial can also be treated as a tutorial for AsyncTask.

As you can see the, the code above has SOAP_ACTION and URL variable, specifying the url and soap action for soap xml web service. After all the the variable instantiations we are creating a HttpPost request, which has soap xml attributes. When the request is sent, response is captured in a string type variable response. Then we use DocumentBuilderFactory methods with SAX parser InputSource class to create a xml document which is used to parse xml response with help from org.w3c.dom.node class. The response XML is stored iteratively in a HashMap called Data.

Next we can pass on this data to onPostExecute method and call method in class from it. Just to show the concept of AsyncTask, I have created a method doWhateverYouWant().

Have a look at the login method web service response:

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
   <s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
      <LoginResponse xmlns="http://tempuri.org/">
         <LoginResult>
            <AUTH_OUTPUT xmlns="">
               <AGENT TA_ID="TR12" OF_ID="TRI98" USER_ID="TR-UID33" CURR_CODE="USD" TA_CL_AMOUNT="6538453.00" TA_ACCOUNT_FREEZED="0"/>
               <CUSTOMER_ID>TR-UID33</CUSTOMER_ID>
               <EMAIL_ID>[email protected]</EMAIL_ID>
               <USER_FIRST_NAME>Truiton</USER_FIRST_NAME>
               <OF_AIRPORT>0</OF_AIRPORT>
               <USER_LAST_NAME></USER_LAST_NAME>
               <EMPLOYEE_CODE>TR110004</EMPLOYEE_CODE>
               <OF_OFFICEID>NYCP0034</OF_OFFICEID>
               <OF_ID>TRI98</OF_ID>
               <POLICY_APPLY>False</POLICY_APPLY>
               <FIRST_APPROVER/>
               <FIRST_APPROVER_CODE/>
               <OF_ADDRESS1/>
               <OF_ADDRESS2/>
               <CURR_CODE>USD</CURR_CODE>
               <APL_TYPE_ID>1</APL_TYPE_ID>
               <DEPT_ID>84</DEPT_ID>
               <DEPT_NAME/>
               <POSITION_NAME/>
               <POSITION_ID>51</POSITION_ID>
               <OF_NAME>Truit Online</OF_NAME>
               <OF_CITY>NY</OF_CITY>
               <CITY_NAME>New York</CITY_NAME>
               <OF_COUNTRY>US</OF_COUNTRY>
               <CO_NAME>United States</CO_NAME>
               <USER_DOB>19750505</USER_DOB>
               <CONTACT_NO>000-0000-000000</CONTACT_NO>
               <MOBILE_NO>001-0000000000</MOBILE_NO>
               <ADDRESS>New York</ADDRESS>
               <STATE_NAME>New York</STATE_NAME>
               <METRO_CITY>
                  <CITY_DETAILS CITY_CODE="BUF" CITY_NAME="Buffalo Niagara International" CO_CODE="US"/>
                  <CITY_DETAILS CITY_CODE="JFK" CITY_NAME="John F Kennedy International" CO_CODE="US"/>
                  <CITY_DETAILS CITY_CODE="LGA" CITY_NAME="La Guardia" CO_CODE="US"/>
                  <CITY_DETAILS CITY_CODE="EWR" CITY_NAME="Newark Liberty International" CO_CODE="US"/>
               </METRO_CITY>
               <ERRORS STATUS="TRUE">
                  <ERROR CODE="" DESC=""/>
               </ERRORS>
            </AUTH_OUTPUT>
         </LoginResult>
      </LoginResponse>
   </s:Body>
</s:Envelope>

One more thing don’t forget to add the internet access permission in the manifest, as while consuming WCF SVC XML web service with attributes on Android we will be interacting with internet.

<uses-permission android:name="android.permission.INTERNET" />

Have a look at the log image:

With this I can conclude my tutorial on consuming WCF SVC XML web service with attributes on Android. Here we sent out a soap request with attributes in tags, which was not looking feasible by use of Ksoap2 library. Therefore we used HttpClient, HttpPost, HttpResponse, and HttpEntity classes of org.apache.http package to create and send the request to WCF SVC XML web service. Next we used DocumentBuilderFactory, DocumentBuilder, StringReader, InputSource, and NodeList classes to parse the data in XML format. Also this approach promotes the use of inbuilt functionality of Android and Java since we did not use any third party library. Hope this tutorial helps you, If it did please +1, like and share on Google+ and Facebook.

About Mohit Gupt

An android enthusiast, and an iPhone user with a keen interest in development of innovative applications.


Leave a comment

Your email address will not be published. Required fields are marked *

3 thoughts on “Consuming WCF SVC XML Web Service with Attributes in Android