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:
<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:
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):
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
:
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) {
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();
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;
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);
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;
}
}