Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web

Android access to .NET Web Service with Object as parameter and return value

4.71/5 (6 votes)
8 Jul 2011CPOL3 min read 39.4K  
Class to access a .NET Web Service with an Object parameter or even an array of objects.

Introduction


Originally, I needed to develop an Android app that can call a .NET Web Service (SOAP based). I searched the net and found ksoap2 as the mostly suggested.
My Web Service is a complex one with parameters ranging from primitive types, date, objects, array of objects,
etc. So I tried to create simple Web Sfirst and then try ksoap2 with increasing complexity.


Well, ksoap2 can call A simple Web Service very good. But then, I find out that I needed to download a patched ksoap2 in order to create an Object parameter.
And that I needed to create the class that implements KvmSerializable first. I needed to declare the methods getProperty and setProperty
with number as the parameter. So, it was awkward for me. Unfortunately, I bumped against another problem. Some of my parameters were declared ByRef in the .NET Web Service.
How do I get that with ksoap2? Well, rather than looking again in the net, I tried to develop my own class to do the job.


Using the code


Let's say, you have this .NET Web Service that you want to call:


VB
<webmethod()>
Public Function LoadTargetItemDistrict(periode As DateTime, ByRef detail() _
       As ZTA_SFA_TIDD, ByRef errMessage As String) As Boolean

Then you can call it from Android using this code:


Java
IvanWebService iws = new IvanWebService("http://10.0.2.2:7788/WSSmartLogic/BLScorecard.asmx");

ZTA_SFA_TIDD[] detail = new ZTA_SFA_TIDD[0];

iws.call("LoadTargetItemDistrict", "periode", "2011-03-01", "detail", detail, "errMessage", "");

ZTA_SFA_TIDD[] tidd = new ZTA_SFA_TIDD[1];
Boolean ret = false;
String errMsg = "";

errMsg = (String)iws.getVariableValue("errMessage", new String().getClass());
ret = (Boolean) iws.getReturnValue(ret.getClass());
tidd = (ZTA_SFA_TIDD[])iws.getVariableValue("detail", tidd.getClass());

The first statement is to create the IvanWebService object with a parameter to your ASMX file. I use IP 10.0.2.2 to access my local computer
from the Android emulator. The next step is to just call your method. The first parameter is the method name. The rest of the parameters are the Web Service parameters
in name, value pairs. So I passed March 2011 as the period and a detail array for the parameter detail. I also passed an empty string as the parameter errMessage.
See that I don't need to pass a variable in errMessage even though errMessage is declared ByRef in the Web Service.


The next step is to get the result back from the call. You use getReturnValue to get the return value from the Web Service. You need to pass
the class of the return value as the only parameter. The method getReturnValue returns an Object therefore you need to cast it as Boolean in this case.
When you want to get the value from ByRef, just use getVariableValue with two parameters. The first is the parameter name, the second is the class
of that parameter. As usual, you need to cast it.


That's all you need to call a Web Service. It was much simpler than my journey with ksoap2. Before I forget, this is the code for the ZTA_SFA_TIDD
class (Target Item District Detail class):


Java
import java.util.*;

public class ZTA_SFA_TIDD {
public Date Periode;
public String MVGR2; 
public String BEZEI;
public String MEINH;
public String ID;
public double Target;
public double Realisasi;
public double PersenRealisasi;
public double Kurang;

public ZTA_SFA_TIDD(){}
}

Yeah, I know that the class is so simple and contains no method other than an empty constructor. You can add methods to your class or even make the member variables
private and create property methods.


As for a note, I haven't parsed int yet in the class code. So if you need it, just change the source code and add it.
The source code is not complicated and it is so obvious where to add.


Finally, this is the code for the class IvanWebService:


Java
package com.ivan;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class IvanWebService {
    private URL mUrl;
    private String mResult;
    private String mMethodName;

    public IvanWebService(String url){
        try {
            mUrl = new URL(url);
        } catch (MalformedURLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    public void call(String methodName,Object... args) throws IOException, 
                     IllegalArgumentException, IllegalAccessException{
        mMethodName = methodName;
        URLConnection conn = mUrl.openConnection();
        conn.setRequestProperty("Content-Type", "text/xml; charset=utf-8");
        conn.addRequestProperty("SOAPAction", "http://tempuri.org/" + methodName);
        conn.setDoOutput(true);
        OutputStreamWriter wr = new OutputStreamWriter(conn.getOutputStream());
        String body = "<?xml version=\"1.0\" encoding=\"utf-8\"?>"  +
            "<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:" + 
            "xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:" + 
            "soap=\"http://schemas.xmlsoap.org/soap/envelope/\">" +
            "<soap:Body>" +
            "<" + methodName + " xmlns=\"http://tempuri.org/\">";
        body += buildArgs(args);
        body += "</" + methodName + ">" +
            "</soap:Body>" +
            "</soap:Envelope>";
        wr.write(body);
        wr.flush();
        // Get the response
        BufferedReader rd = new BufferedReader(new InputStreamReader(conn.getInputStream()));
        mResult = "";
        String line;
        while ((line = rd.readLine()) != null) {
            mResult += line;
        }
        wr.close();
        rd.close();
    }

    private String buildArgs(Object... args)
            throws IllegalArgumentException, IllegalAccessException{
        String result = "";
        String argName = "";
        for(int i=0;i<args.length;i++){
            if(i % 2 == 0) {
                argName = args[i].toString();
            }
            else{
                result += "<" + argName + ">";
                result += buildArgValue(args[i]);
                result += "</" + argName + ">";
            }
        }
        return result;
    }

    private String buildArgValue(Object obj)
            throws IllegalArgumentException, IllegalAccessException{
        Class<?> cl = obj.getClass();
        String result = "";
        if(cl.isPrimitive()) return obj.toString();
        if(cl.getName().contains("java.lang.")) return obj.toString();
        if(cl.getName().equals("java.util.Date")){
            DateFormat dfm = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z");
            return dfm.format((Date)obj);
        }

        if(cl.isArray())
        {
            String xmlName = cl.getName().substring(cl.getName().lastIndexOf(".") + 1);
            xmlName = xmlName.replace(";", "");
            Object[] arr = (Object[])obj;
            for(int i=0; i< arr.length; i++)
            {
                result += "<" + xmlName + ">";
                result += buildArgValue(arr[i]);
                result += "</" + xmlName + ">";
            }
            return result;
        }
        Field[] fields = cl.getDeclaredFields();
        for(int i=0;i<fields.length;i++)
        {
            result += "<" + fields[i].getName() + ">";
            result += buildArgValue(fields[i].get(obj));
            result += "</" + fields[i].getName() + ">";
        }
        return result;
    }

    public String getResult(){
        return mResult;
    }

    public Object getReturnValue(Class<?> cl) throws IllegalAccessException, 
                  InstantiationException, ParseException
    {
        return getVariableValue(mResult, mMethodName + "Result", cl);
    }

    public Object getVariableValue(String name, Class<?> cl) 
           throws IllegalAccessException, InstantiationException, ParseException
    {
        return getVariableValue(mResult, name, cl);
    }

    private Object getVariableValue(String body, String name, Class<?> cl) 
            throws IllegalAccessException, InstantiationException, ParseException
    {
        int start = body.indexOf("<" + name + ">");
        start += name.length() + 2; //with < and > char
        int end = body.indexOf("</" + name + ">");
        if(end == -1)
            body = "";
        else
            body = body.substring(start, end);
        if(cl.getName().toLowerCase().contains("string")) return body;
        if(cl.getName().toLowerCase().contains("double")) return Double.parseDouble(body);
        if(cl.getName().toLowerCase().contains("date")){
            DateFormat dfm = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            return dfm.parse(body.replace("T"," "));
        }
        if(cl.getName().toLowerCase().contains("boolean"))
            return Boolean.parseBoolean(body);
        if(cl.isArray())
        {
            if(body == "")
                return Array.newInstance(cl.getComponentType(), 0); //return empty array
            String xmlName = cl.getName().substring(cl.getName().lastIndexOf(".") + 1);
            xmlName = xmlName.replace(";", "");
            String[] items = body.split("</" + xmlName + ">");
            Object arr = Array.newInstance(cl.getComponentType(), items.length);
            for(int i=0;i<items.length;i++)
            {
                items[i] += "</" + xmlName + ">";
                Array.set(arr, i, getVariableValue(items[i], xmlName, cl.getComponentType()));
            }
            return arr;
        }
        Object result = cl.newInstance();
        Field[] fields = cl.getDeclaredFields();
        for(int i=0;i<fields.length;i++)
        {
            fields[i].set(result, getVariableValue(body, fields[i].getName(), fields[i].getType()));
        }
        return result;
    }
}

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)