Web Service That Returns An Array of Objects With KSOAP



In my previous post, I wrote about an example of passing complex objects with KSOAP. In this post, I will write about returning arrays of objects with KSOAP.
If you want to know how to write a method that returns an array of complex objects, look at this code:
public static Category[] GetAllCategories()
    {
        String MethodName = "GetAllCategories";
        SoapObject response = InvokeMethod(URL,MethodName);
        return RetrieveFromSoap(response);
        
    }

Where the function InvokeMethod is :

public static SoapObject InvokeMethod(String URL,String MethodName)
    {
        SoapObject request = GetSoapObject(MethodName);
        SoapSerializationEnvelope envelope = GetEnvelope(request);
        return  MakeCall(URL,envelope,NAMESPACE,MethodName);
    }

GetSoapObject() and GetEnvelope() are:

public static SoapObject GetSoapObject(String MethodName)
    {
        return new SoapObject(NAMESPACE,MethodName);
    }
    public static SoapSerializationEnvelope GetEnvelope(SoapObject Soap)
    {
        SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);
        envelope.dotNet = true;
        envelope.setOutputSoapObject(Soap);
        return envelope;
    }

MakeCall() is :
/**
     * 
     * @param URL - The complete URL where the web service resides 
     * @param Envelope - The envelope to be passed
     * @param NAMESPACE - The web method namespace
     * @param METHOD_NAME - The method name
     * @return - SoapObject containing the resultset
     */
    public static SoapObject MakeCall(String URL, SoapSerializationEnvelope Envelope, String NAMESPACE, String METHOD_NAME)
    {
        AndroidHttpTransport androidHttpTransport = new AndroidHttpTransport(URL);
         try
            {
                androidHttpTransport.call(NAMESPACE + METHOD_NAME, Envelope);
                SoapObject response = (SoapObject)Envelope.getResponse();
                return response;
            }
         catch(Exception e)
         {
             e.printStackTrace();
             
         }
         return null;
    }

The most important part for retrieving the actual array of objects (in this case Category objects) is the following:


/**
     * 
     * @param soap - represents the entering Soap object
     * @return returns the list of categories extracted from the response
     */
    public static Category[] RetrieveFromSoap(SoapObject soap)
    {
        Category[] categories = new Category[soap.getPropertyCount()];
        for (int i = 0; i < categories.length; i++) {
            SoapObject pii = (SoapObject)soap.getProperty(i);
            Category category = new Category();
            category.CategoryId = Integer.parseInt(pii.getProperty(0).toString());
            category.Name = pii.getProperty(1).toString();
            category.Description = pii.getProperty(2).toString();
            categories[i] = category;
        }
        return categories;
    }

NOTE: Do not use VECTOR class type provided by KSOAP! It does not work.

I really copy-pasted this from Eclipse, but hope it helps. Please write if you need more help. I hope I saved you at least some nerves.

All the best.

146 comments:

Yanis said...

hi, i follow step by step the first article (return of one complex type) and its works perfect. So i decided to try this one but i have everytime a xmlPullParserException.

this is my code :

SoapObject request = GetSoapObject(METHOD_NAME);
SoapSerializationEnvelope envelope = GetEnvelope(request);


HttpTransportSE androidHttpTransport = new HttpTransportSE(URL);

try {
androidHttpTransport.call(SOAP_ACTION, envelope);

SoapObject response = (SoapObject) envelope.getResponse();
Filiere[] Allrooms = RetrieveFromSoap(response);

ACTV.setHint(Allrooms[0].titre);
} catch (Exception e) {
e.printStackTrace();
}
}
public static SoapObject GetSoapObject(String MethodName)
{
return new SoapObject(NAMESPACE,MethodName);
}
public static SoapSerializationEnvelope GetEnvelope(SoapObject Soap)
{
SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);
envelope.dotNet = true;
envelope.setOutputSoapObject(Soap);
return envelope;
}

public static Filiere[] RetrieveFromSoap(SoapObject soap)
{
Filiere[] categories = new Filiere[soap.getPropertyCount()];
for (int i = 0; i < categories.length; i++) {
SoapObject pii = (SoapObject)soap.getProperty(i);
Filiere category = new Filiere();
category.fichier = pii.getProperty(0).toString();
category.titre= pii.getProperty(1).toString();
categories[i] = category;
}
return categories;
}



}
===========================================
public class Filiere implements KvmSerializable{

public String titre;
public String fichier;
public Object getProperty(int index) {
// TODO Auto-generated method stub
switch(index)
{
case 0:
return titre;
case 1:
return fichier;
}

return null;
}
public int getPropertyCount() {
// TODO Auto-generated method stub
return 2;
}
public void getPropertyInfo(int index, Hashtable properties,
PropertyInfo info) {
// TODO Auto-generated method stub
switch(index)
{
case 0:
info.type = PropertyInfo.STRING_CLASS;
info.name = "titre";
break;
case 1:
info.type = PropertyInfo.STRING_CLASS;
info.name = "fichier";
break;
default:break;
}


}
public void setProperty(int index, Object value) {
switch(index)
{
case 0:
titre = value.toString();
break;
case 1:
fichier = value.toString();
break;
default:
break;
}
}


}

===========================================
My webservice dotnet:
public Filiere[] GetAllRooms();

thanks for your help

SeeSharpWriter said...

You are probably missing this line:

envelope.addMapping(NAMESPACE, "Filiere",new Filiere().getClass());

before this line:
androidHttpTransport.call(SOAP_ACTION, envelope);

KSOAP needs to know what to do with the response once it gets an array of complex objects. That line tells it that when "Filiere" comes in SOAP the response, it needs to construct new objects of the Filiere class.

Anonymous said...

Thanks this help a lot

Anonymous said...

Hi,
I am working with ksoap and trying to get the normal XML response from the server as it is but it gives me OutOfMemory exception at line androidHttpTransport.call(SOAP_ACTION, envelope)
The response that i am trying to get from server is around 1.4Mb. Could this be the reason of this exception? Plz help..I am struggling with this since 3 days..

SeeSharpWriter said...

This could be because the virtual machine for android does not have that much memory. Try to increase it first.

Second of all, try to get a response from the server that returns smaller dataset to check whether it returns results as expected.

Just as another idea is to separate the response on smaller chunks and then merge them on Android side.

Hope this helps.

Anonymous said...

Firstly, thanks a lot for your reply :)

I have tried it with web service that returns smaller data sets and it works perfectly fine with that.
I think the only problem here is the size of the response but I cant redo my web service :( can you suggest any other alternative??

SeeSharpWriter said...

One thing that comes to my mind is to write a proxy web service which will invoke your original service and pass the results to Android via KSOAP in smaller chunks of data.

Or maybe there is some setting in the Android itself for adjusting the maximum receivable data, but I am not sure where could you investigate that.

lerato said...

Hi, I have a working SOAP function in my Android app and have it outputting its result to a textview, when the user clicks a button.

How can I implement the array function? I'm unsure how much of the code to use from this page, and from the previous page. I found I have to include some of the other code to remove the errors.

Also how do i get the returned result to then get sent to another activity?

Thanks for your help, couldn't have got my SOAP example working with the first info.

SeeSharpWriter said...

lerato, you only need the definition of the Category class and definition of the static variables such as URL from the previous post.

If you look closely, the code that invokes the web service is now wrapped in separate functions, while in the previous post it was one big invoke.

Regarding transferring data to another activity, you may consider resources like these:

http://developerlife.com/tutorials/?p=302

http://www.balistupa.com/blog/2009/08/passing-data-or-parameter-to-another-activity-android/

Md.Rezaul Hoque said...

hi..
Thanx, it is a nice post.
I am new to android so it helps me a lot.
Can you plz tell me how do I bind the categories to a Listview??

SeeSharpWriter said...

Hi Md.Rezaul,

have you already managed to retrieve the categories from the web service ?

Md.Rezaul Hoque said...

Hi
No, I think I have not managed to retrieve data yet. But I will be able to do that after I play a little bit more with it.
But after retrieval, I need to bind them to a Listview.
How do I do that?
My service returns a set of data (i.e. 10/12 rows).

Kiran said...

I want to display the retrieved data in a list (ArrayList or ArrayAdapter). How do I do this?

SeeSharpWriter said...

After pulling the data from the webservice, simply copy each item in ArrayList

Anonymous said...

i m getting exception of class cast exception ...
first article is workng fine for me ... but getting this exception in second article ....

SeeSharpWriter said...

On which line you are getting exception? What does your webservice return ?

Anonymous said...

Hey, Im getting error as well. My Webservice return .NET dataset, is it possible?

SeeSharpWriter said...

I'm not sure if it is possible, probably is, but datasets are more complex objects. Perhaps it woudl be more manageable for you to copy them in an array of business objects within the web service. Then the Android client will work with business objects and will not be aware nor care whether they come from a database, XML or the outer space.

DoUbLeR said...

This tutotial is great!! I have another problem:

Create a SOAP REQUEST with an array of string... STRING[]. I have red these tutorial but i don't know how i can do this. Can you help me?

Thank you!
Roberto

SeeSharpWriter said...

Roberto, does your web service return string[] or take string[] as an argument?

Paraskevi said...

Hello,
i have used this code but my problem is that the returned data are only the first entry of my object. I dont know if understand what i mean.
My code is:
...
try
{
envelope.addMapping(NAMESPACE, "Category",new Category().getClass());
androidHttpTransport.call(HelloWorld_SOAP_ACTION, envelope);

SoapObject response = (SoapObject) envelope.getResponse();

Category[] Allrooms = RetrieveFromSoap(response);


for(int i= 0; i< Allrooms.length ; i++)
{
welcome.setHint(Allrooms[i].Title);

}
}

SeeSharpWriter said...

Paraskevi,

how does your web service look like?
Please send that code too.

Paraskevi said...

Hello again, the code of webservice is:

Public Class LocationInfo
Public strTitle As String
Public strLatitude As String
Public strLongitude As String
End Class

_
Public Function Location() As LocationInfo()

Dim dt As New DataTable()

Dim strConnectionString As String
strConnectionString = "xxxxx"

Dim myconnection As SqlConnection = New SqlConnection(strConnectionString)
Dim mycommand As SqlDataAdapter = New SqlDataAdapter("SET NOCOUNT ON SELECT lm_name, lm_latitude, lm_longitude from locations_main", myconnection)

Dim ds As New DataSet
mycommand.Fill(ds, "data")

Dim objTitles As LocationInfo() = New LocationInfo(ds.Tables(0).Rows.Count - 1) {}


Dim intRsCount As Int16
For intRsCount = 0 To ds.Tables(0).Rows.Count - 1
objTitles(intRsCount) = New LocationInfo
objTitles(intRsCount).strTitle = ds.Tables(0).Rows(intRsCount)("lm_name")
objTitles(intRsCount).strLatitude = ds.Tables(0).Rows(intRsCount)("lm_latitude")
objTitles(intRsCount).strLongitude = ds.Tables(0).Rows(intRsCount)("lm_longitude")
Next

Return objTitles

End Function

Paraskevi said...

I have 2 entries in my table locations_main, but with your example i can take only the second entry of my table locations_main.
thanks a lot,

Paraskevi

SeeSharpWriter said...

Paraskevi,

you should have a class LocationInfo on the Android side as well - it should have the exact same definition as the LocationInfo class on the server side.

Also in the Android code your line should look like this:

envelope.addMapping(NAMESPACE, "LocationInfo",new LocationInfo().getClass());

Also don't forget to make your LocationInfo class implement KVMSerializable interface.

Paraskevi said...

Hello SeeSharpWriter,

I have done this !! but i continue take only the last entry of my table locations_main.
I think that the problem is at :
for(int i= 0; i< Allrooms.length ; i++)
{
welcome.setHint(Allrooms[i].Title);

}

Anonymous said...

Woah Great work. it really helps alot :D
hmm may i know how do you update the data and pass it back to your web service? Thanks

SeeSharpWriter said...

How do you mean "update the data" ?
You basically set the attributes to your object and pass it to the web service - take a look of my post:

http://seesharpgears.blogspot.com/2010/10/ksoap-android-web-service-tutorial-with.html

Hope this is what you meant.

robarg said...

Hi! First of all, thank you for this great article.

I have the following problem:

I have a Webservice method that returns a complex type (CategoriesResponse) that has an Array of Categories (Category[]) and an integer Response Code.

I get a class cast exception during the soap.GetProperty. However if I modify my webservice to return just a Category[] and not a complex CategoriesResponse, it works!

Is there any way to make this work with a complex type like CategoriesResponse?

Thanks in advance!!!
John.

SeeSharpWriter said...

Hi John,

I have no working example of that case. I am sorry, it could be some trick with the Vector class, but I have no working code to show you.

If you happen to solve this, please post the solution here, it would be very helpful to everyone developing on Android

Paraskevi said...

Hello SeeSharpWriter ,
i have stuck with that problem many days.
In my android app i have one TabHost with two tabs.
At the first tab, call a function (Public Function GetNearbyVideo() As Videos()) from webservice and at
the second tab call the (Public Function PutFile()) function from webservice.
But whene i change through tabs when i push the second tab it called first the GetNearbyVideo() function and then the PutFile() function
but i want called only the PutFile() function.
I dont know what to do.
Maybe there is a problem with activities? Must stop the activity in each tab?
Please help me if you know something about that!
Thanks a lot

Malk said...

SeeSharpWriter,
your example worked perfectly
I'm trying to put an array of status in the category, but can not return its value.

SoapObject piii = (SoapObject)pii.getProperty(3);
Status[] sts = new Status[piii.getPropertyCount()];
for (int y = 0; y < piii.getPropertyCount(); y++) {
Status s = new Status();
s.StatusId = Integer.parseInt(piii.getProperty(0).toString());
s.Name = piii.getProperty(1).toString();
sts[y] = s;
}
category.Sstatus = sts;
categories[i] = category;

I changed only this code and create a class for Status like Categorie class.

ren said...

Hey SeeSharp
I have to get data from a web service, so I send a string and then this web service returns an object array (foo[]).
but when I receive this object, it is says it :
java.lang.ClassCastException: org.ksoap2.serialization.SoapPrimitive
I have 2 cases. Or I receive the first object of this array in a String[] or I get this error.
I cannot get the complex object array, i do not know why!!
could you take a look and tell me what is wrong?
this is my code..

public static PontosMunicipais[] getAllPontosMunicipais(String cidade){
String METHOD_NAME = "getMunicipios";
SoapObject response = invokeMethod(URL, METHOD_NAME, cidade);
return retrieveFromSoap(response);
}
public static SoapObject invokeMethod(String URL, String methodName, String cidade){
SoapObject request = getSoapObject(methodName);
PropertyInfo pi = new PropertyInfo();
pi.setName("cidade");
pi.setValue(cidade);
pi.setType(String.class);

request.addProperty(pi);
SoapSerializationEnvelope envelope = getEnvelope(request);
return makeCall(URL, envelope, NAMESPACE, methodName);
}
public static SoapObject getSoapObject(String MethodName){
return new SoapObject(NAMESPACE, MethodName);
}
public static SoapSerializationEnvelope getEnvelope(SoapObject soap){
SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);
envelope.dotNet= true;
envelope.setOutputSoapObject(soap);
return envelope;
}
public static SoapObject makeCall(String URL, SoapSerializationEnvelope envelope, String nameSpace, String methodName){
AndroidHttpTransport transport = new AndroidHttpTransport(URL);
try {
transport.call(nameSpace + methodName, envelope);
SoapObject response = (SoapObject)envelope.getResponse();
return response;
} catch (Exception e) {
e.printStackTrace();
System.out.println("Erro makeCall()");
}
return null;

}
public static PontosMunicipais[] retrieveFromSoap(SoapObject response){
PontosMunicipais[] pontosMunicipais = new PontosMunicipais[response.getPropertyCount()];

for (int i = 0; i < pontosMunicipais.length; i++) {

Object sO = (Object) response.getProperty(i);

SoapObject pii = (SoapObject) response.getProperty(i);


PontosMunicipais cidade = new PontosMunicipais();
cidade.setGeocodigo(pii.getProperty(0).toString());
cidade.setGid(Long.parseLong(pii.getProperty(1).toString()));
cidade.setId_uf(pii.getProperty(2).toString());
cidade.setLatitude(Double.parseDouble(pii.getProperty(3).toString()));
cidade.setLongitude(Double.parseDouble(pii.getProperty(4).toString()));
cidade.setNome(pii.getProperty(7).toString());

pontosMunicipais[i] = cidade;
}

return pontosMunicipais;
}

SeeSharpWriter said...

I think you should enable debug mode and use the debugger of Eclipse to see how objects you receive are encapsulated. Also in order to work with Double as data type you need to implement a class with Marshal interface and register it for the SOAP envelope

Megha Sanghvi said...

hi
i have one problem regarding SOAP call in android using KSOAP.in my webservice sometime data may not be come so each time i extract data from webservice i have to check if its blank tag or not?
so how to test this condiion using KSOAP in android?

i check using getProperty but how to it return name of SOAPObject to anyType not the name of tag

SeeSharpWriter said...

Yes your return type should contain an attritbute isLoaded for example. You will extract this tag first and check its value. If it is true you will proceed to the rest of the arguments. Also you can specify default values and you can check against them on the Android side.

ramesh said...

how to get a images,videos data set from .net web service to android.
Please send the code to my email id:
ramesh_kumar740@yahoo.com

VU Hong Phat said...

Thank you for helpful tutorial.
It helped me a lot.
But, if you want to it run correctly, plz replace the statement :
SoapObject response = (SoapObject) Envelope.getResponse();
By:
SoapObject response = (SoapObject) Envelope.bodyIn;

Cadu said...
This comment has been removed by the author.
Cadu said...

Guys, this isa fine example however the getpropertycount method in the SoapObject is retuning 1...I cant get past that

Look at my code

SoapObject resultSO = (SoapObject)envelope.getResponse();

products[] categories = new products[resultSO.getPropertyCount()];

int cnt=categories.length; // Result= 1

Please help I have been stuck in this for weeks

SeeSharpWriter said...

Cadu, please provide more insight by pasting a larger portion of your code (such as how do you invoke the service and the .NET web service signature as well

Cadu said...

SoapObject request = new SoapObject(NAMESPACE, METHOD_NAME);
SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER10);
envelope.dotNet=true;
envelope.setOutputSoapObject(request);
envelope.addMapping(NAMESPACE, "products",new products().getClass());

HttpTransportSE androidHttpTransport = new HttpTransportSE(URL);
androidHttpTransport.call(SOAP_ACTION, envelope);

SoapObject resultSO = (SoapObject)envelope.getResponse();

There are two alternatives:

1. If I try to get the count of the ResultSO object I get 1
2. If I try to get the count of the Pii Obect I get 1800 which I think its the length of my big string (its an array return value)

Method 1
products[] categories = new productsresultSO.getPropertyCount()];
int cnt=categories.length; //Returns 1



Method 2
SoapObject pii = (SoapObject)resultSO.getProperty(0);
products[] categories = new products[pii.getPropertyCount()];
int cnt=categories.length; //Return 1800

I dont have 1800 product objects in my XML

SeeSharpWriter said...

Maybe you should use the debugger to inspect the object and see what is there inside it. Try inspecting for BodyIn attribute.

Anonymous said...

Hi there,
congratulation for this tutorial, it´s very useful. I´m trying to develop a WS using KSOAP 2 and I´m with problem to send complex objects containing complex objects list. Here´s my class:

public class TipoUsuario implements KvmSerializable {

private Integer id;
private String descricao;
private String tipoRegistro;
private List permissaoList;

// constructors and getters and setters

public Object getProperty(int index) {
switch (index) {
case 0:
return id;
case 1:
return descricao;
case 2:
return tipoRegistro;
case 3:
return permissaoList;
default:
return null;
}
}

public int getPropertyCount() {
return 4;
}

@SuppressWarnings("rawtypes")
public void getPropertyInfo(int index, Hashtable arg1, PropertyInfo info) {
switch (index) {
case 0:
info.type = PropertyInfo.INTEGER_CLASS;
info.name = "id";
break;
case 1:
info.type = PropertyInfo.STRING_CLASS;
info.name = "descricao";
break;
case 2:
info.type = PropertyInfo.STRING_CLASS;
info.name = "tipoRegistro";
break;
case 3:
info.type = PropertyInfo.VECTOR_CLASS;
info.name = "permissaoList";
break;
}
}

public void setProperty(int index, Object value) {
switch (index) {
case 0:
id = Integer.parseInt(value.toString());
break;
case 1:
descricao = value.toString();
break;
case 2:
tipoRegistro = value.toString();
break;
case 3:
getPermissaoList().add(
((Permissao) Utils.soapToObject((SoapObject) value,
Permissao.class)));
break;
}
}
}

Where, Utils.soapToObject converts SoapObject to some Class through reflection API.

Permissao has only primitives attributes.

My doubt: How can I map an array (List), in this case, permissaoList.

Thanks in advance.

SeeSharpWriter said...

I see what the problem is: the List attribute. So far I have never succeeded to send and receive complex objects with nested arrays or lists.

My advice would be to use String attribute instead and populate it with XML string. Then you can easily parse it on .NET's side.

Anonymous said...

Thanks for quick response!
Strange that I could get a list, but I couldn't send through KSOAP. My WS was developed in Java.
I´ll try using Vector instead List, maybe works.

Regards,
Roni

neo3 said...

Hey Guys,

I'm new to Android and kSoap and need a little help ;)

I successfully implemented kSoap2. First big step for me. But the next one is confusing me much more ^^

As return statement of my request I get a SoapObject which contains a DataSet:

pls take a look at:

http://www.android-hilfe.de/android-codeschnipsel/152620-ksoap-zugriff-auf-object-vom-typ-dataset.html

If i print the SoapObject, I'm getting this content:

http://www.android-hilfe.de/attachments/android-codeschnipsel/52338d1317905506-ksoap-zugriff-auf-object-vom-typ-dataset-ksoap.png

So now I want to parse the data like CARPOINT_ID, CARPOINT_X etc. into valid java objects (ArrayList, Array,...)

Can anyone help me with the first step?

neo3 said...

PS: Thanks a lot :):)

SeeSharpWriter said...

Hi neo3,

I do not know how can you parse the DatSet from KSoap. One way to go is to write a DataSet class on the Android's side but I really do not recommend it.

Instead, re-write your web service so that it returns an array of custom objects ( Carpoints[] for example) and then write a simple Carpoint class which you can map it the same way I am doing it in this method:

public static Category[] RetrieveFromSoap(SoapObject soap)

Cadu said...

seesharp,

My main issue here is that when I receive the envelope (via getresponse) I assign the property 0 to another SoapObject and try to count it, it returns 1800...I examined this soap Object (PII) and the problem is that it keeps repeting the same elements (content in xml) over and over again, that is why I get such a high count.

See example:
SoapObject pii = (SoapObject)resultSO.getProperty(0);
products[] categories = new products[pii.getPropertyCount()]; //1800 objects


On the other side, if I try to examine the bodyIn I get count 1..so I cant work with it....

What do you think?

SeeSharpWriter said...

Yes, someone mentioned that using BodyIn works for them so you should try that out. Please share your further experience.

SeeSharpWriter said...

Roni, please share whether the Vector class works for your Java based web service.

Akshatha Kiran said...

Hi SeeSharpWriter,

I read the above comments werin u have stated - "So far I have never succeeded to send and receive complex objects with nested arrays or lists. "

I am working on those lines..I am using ksoap2 and want to send and receive complex objects eg: CategoryItems (Array Of Category)

How would you suggest me to move further using ksoap2 based on ur experience on the same??

Akshatha Kiran said...

Hi,

Finally I got solution for my above question..

Here goes the code..Hope it would help lot of people out there..

In order for the below code to work - make a change in your webservice..while sending the data before adding the objects in the array from .net fill the count property like below..

cateItems.Count = NoOfItemsInArray;//cateItems is the object Of ArrayList - in my case its CategoryItems..

And here goes the code in Android..

public static Category[] RetrieveFromSoap(SoapObject soap)
{
SoapObject ObjSoap = (SoapObject)soap.getProperty(0);//change here..added this line

Category[] categories = new Category[Integer.parseInt((ObjSoap.getProperty(0).toString()))];//Chnage here..It gives //the count of items sent from Web Service Call..

//Category[] categories = new Category[soap.getPropertyCount()];//commented this line..

for (int i = 0; i < categories.length; i++)
{
SoapObject pii = (SoapObject)ObjSoap.getProperty(i+1);//change here.. i+1..as we dont want the data at the '0'th //position..which has the count retrieved above..

Category category = new Category();

category.CategoryId = Integer.parseInt(pii.getProperty(0).toString());
category.Name = pii.getProperty(1).toString();
category.Description = pii.getProperty(2).toString();

categories[i] = category;
}

return categories;
}


//And Now as I have the data in categories array
//will loop through the data and print accordingly..

//after the below call

CategoryList[] catitems = GetAllCategories();

//Print Data..

String sData = "";

for(int i= 0; i < catitems.length ; i++)
{
Category C = (Category)catitems[i];


sData = sData + " " + "CategoryId: " + C.CategoryId + " Name: " + C.Name + " Description " + C.ParentTypeID + "\n";

}

tv.setText(sData);


//Data is obtained item wise in Array :) :)

Akshatha Kiran said...

Hi,


Finnaly got solution to my above question..Here it goes..Hope it helps out many out there..


For below code to work make change in your web service call before sending the array list as below..

This should be added before adding the items in the array..

..In .NET


cateItems.Count = NoOfItemsInArray;//cateItems is the object Of Array..CategoryItems in my case


..ANDROID Code..


public static Category[] RetrieveFromSoap(SoapObject soap)
{
SoapObject ObjSoap = (SoapObject)soap.getProperty(0);//change here..added this line

Category[] categories = new Category[Integer.parseInt((ObjSoap.getProperty(0).toString()))];//Chnage here..It gives //the count of items sent from Web Service Call..

//Category[] categories = new Category[soap.getPropertyCount()];//commented this line..

for (int i = 0; i < categories.length; i++)
{
SoapObject pii = (SoapObject)ObjSoap.getProperty(i+1);//change here.. i+1..as we dont want the data at the '0'th //position..which has the count retrieved above..

Category category = new Category();

category.CategoryId = Integer.parseInt(pii.getProperty(0).toString());
category.Name = pii.getProperty(1).toString();
category.Description = pii.getProperty(2).toString();

categories[i] = category;
}

return categories;
}


//And Now as I have the data in categories array
//will loop through the data and print accordingly..

//after the below call

CategoryList[] catitems = GetAllCategories();

//Print Data..

String sData = "";

for(int i= 0; i < catitems.length ; i++)
{
Category C = (Category)catitems[i];


sData = sData + " " + "CategoryId: " + C.CategoryId + " Name: " + C.Name + " Description " + C.ParentTypeID + "\n";

}

tv.setText(sData);


//Data is obtained item wise in Array :) :)

neo3 said...

Thanks a lot @ SeeSharpWriter!!!!

I got the da** thing working :)

SeeSharpWriter said...

Glad to hear that! Would you like to share how did you get it to work?

neo3 said...

Yes, I will. later. ;)

I even wrote everything down and just as I wanted to post, my Screen got black and Firefox had to restart. I wasn't able to restore at least one char of my text :-/

So I'll answer later!

Short answer: I did it, like u suggested. Created a class "Carpoint".

cya

neo3 said...

Hi,

here is the description. Unfortunalley I can't paste the complete code, because it's for my research work :)


- Android (Java, ksoap2):
My first step was creating a class containing the webservice. In this class I added the "RetrieveFromSoap" method, almost similar to yours. I just made some small changes to make it work with my objects (4 Properties, etc).
Then I created a class for the cartesian points, I want to work with. This class (CartesianPoints) contains all attributes I need (Coordinates X,Y,Z as doubles and ID as Integer) and implements the kvmserializable. So it's nearly equal to your "Category" class.

- Webservice (VB.Net):
Here I implemented an equivalent class to the one in Java (Android). So I created a "CartesianPoint" class with same attributes X,Y,Z,ID.
Finally I implemented a WebMethod which returns an Array of "CartesianPoint"s, I can work with in Android.


Tell me, if there are any more questions!


cya

enrmarc said...

Thanks man, but it doesn't work for me. This works:

...
SoapObject response = (SoapObject)envelope.bodyIn;
Person[] list = newPerson[response.getPropertyCount()];
for (int i = 0; i < list.length; i++) {
Person person = (Person)response.getProperty(i);
list[i] = person;
}
...

Where Person is a POJO.
I'm using ksoap2 version 2.5.8. I hope this helps.

SeeSharpWriter said...

Hi enmarc,

several people reported this behavior - I think there might be a slight version in the KSOAP version you might be using, but I am not sure.

Balamurugan said...

Hi friends, I am new to android, now i have a doubt in returns an array of complex objects, with the help of your post i had cleared that

But i have few more doubt in getting response,

if i send below as response

public Category[] GetCategoryById(Category C)
{
Category cc=new Category();
cc.CategoryId=1;
cc.Name="wwww";
cc.Description="XXX";

Category cc1=new Category();
cc1.CategoryId=2;
cc1.Name="RRRR";
cc1.Description="TTT";

Category[] ct = new Category[]{cc,cc1};
return ct;
}

how can i get the response from client side
if i use the below code..

Category[] categories = new Category[soap.getPropertyCount()];
for (int i = 0; i < categories.length; i++) {
SoapObject pii =(SoapObject)soap.getProperty(i);
Category category = new Category();
category.CategoryId = Integer.parseInt(pii.getProperty(0).toString());
category.Name = pii.getProperty(1).toString();
category.Description = pii.getProperty(2).toString();
categories[i] = category;

how can i get the data from category object as one by one to set the value to textview..

Thanks friend...

Anonymous said...

OK, Perfect, Very Good, Very Very Very Good and Clear, OK, OK, OK

Thank you very much

James said...

First off, great post, helped me get started.

However, I may just be being naive, but I'm struggling to see the benefit here. I don't see what how you avoid the "donkey work" since you essentially have to step through each property and place it into your hierarchy of POJOs.

I thought, that with the use of KvmSerializable you could pass the soapObject somewhere and it would know how to parse it, but essentially you still have to do all of the getProperty() etc, so you can scrap the implements interface and it still works..

Any advice?

SeeSharpWriter said...

Perhaps you can improve the code by using Reflection to discover all properties and map to your business object.

Zahir Khan said...

Hi SeeSharpWriter,
Need some help. I tried implementing this but I get one of the below 2 issues:

1. When I use
SoapObject response = (SoapObject)envelope.getResponse()
the length of my Class array is returned as 0

2. When I use SoapObject response = (SoapObject)envelope.inBody
I get the error ArrayIndexOutOfBoundsException length=0; index=0
at statement
SoapObject pii = (SoapObject)soap.getProperty(i);
Book book = new Book();
book.bookName = pii.getProperty(0).toString();

Note that I do have the statement
envelope.addMapping(namespace, "Book", new Book().getClass());
httpTransport.call(namespace + methodname, envelope);

:)

I have tried changing the ksoap version to 2.4, 2.5.8, 2.6.0 to no avail. Any ideas what I am missing?

Also, I test the application directly on my handset connected to the machine. How can I debug the code while running the application. Sorry but I am from .Net background so do not know much about Java :) learning learning learning

Regards,
Zahir Khan

Dexor said...

I am beginner in java programming and android under eclipse and I'm using ksoap to call a web service login and others, do not quite understand where I should start creating classes that you mention here, since the order for the code does not seem clear, If possible you to get into the whole structure of how your code works I would greatly appreciate it, Greetings!

SeeSharpWriter said...

Hi Dexor,

I'm afraid I do not have the whole source code at the moment. I had some unlucky formatting of my disk :(
But you should check the other posts I have written to help you:

http://seesharpgears.blogspot.com/2010/11/basic-ksoap-android-tutorial.html

http://seesharpgears.blogspot.com/2010/10/ksoap-android-web-service-tutorial-with.html

Basically you can organize the code whatever you feel comfortable with. This is not a strict code structure.

Anonymous said...

Hi can you please write a web method in C# which retrieves array of objects, the same like you wrote on the android side.Sorry if my english sucks

Dexor said...

Thanks you so much for the fast answer Sharp, i will learn more in your other blogs. :)

Anonymous said...

please i need help. I wrote a webmethod to retrieve dataset which i am using for passing data to ksoap and i get stucked.

My web method looks like this:

[WebMethod]
public DataSet dohvatiSveDrzave()
{
string connCS = ConfigurationManager.ConnectionStrings["myCS"].ConnectionString;
DataSet dsInfo = new DataSet();
SqlCommand cmd = new SqlCommand();
SqlDataAdapter adapter = new SqlDataAdapter();
SqlConnection conn = new SqlConnection(connCS);
string query = "selectAll";

try
{
cmd.CommandText = query;
cmd.CommandType = CommandType.StoredProcedure;

cmd.Connection = conn;

adapter.SelectCommand = cmd;

adapter.Fill(dsInfo, "Country");
return dsInfo;
}
catch (Exception ex)
{
System.Web.HttpContext.Current.Response.Write(ex.Message);
return null;
}
finally
{
conn.Close();
}

}

can you please tell me why this is not working and how must look a method which returns array of objects. I would like to try this example with ksoap which returns array of objects but i cannot when method in web service isn't working.
I will really apreciate your help.Thanks in advance

SeeSharpWriter said...

Please do not use Dataset as return type because it is fairly complex object. Instead, loop through it and create a list of your business objects and return it.

Anonymous said...

Hi SeeSharpWriter
I followed your sample for returning array of objects and i get stucked. The problem is it returns only one entry from array. I'am struggling with this about 3 days and my head is going to explode. Can you please see where i messed up. This is my full code

.net side
[WebMethod]
public Drzava[] GetAllDrzavaByArray()
{
string connCS = ConfigurationManager.ConnectionStrings["mojCS"].ConnectionString;
Drzava dr = null;

SqlDataAdapter adapter = new SqlDataAdapter();
using (SqlConnection conn = new SqlConnection(connCS))
{
using (SqlCommand cmd = conn.CreateCommand())
{
cmd.CommandText = "prikazi";
cmd.CommandType = CommandType.StoredProcedure;

conn.Open();

using (SqlDataReader reader = cmd.ExecuteReader())
{
while (reader.Read())
{
dr = new Drzava();
dr.IDDrzava = (int)reader["IDDrzava"];
dr.NazivDrzave = (string)reader["NazivDrzave"];
listaDrzava.Add(dr);
}
}
}
}
return listaDrzava.ToArray();

}

[Serializable]
public class Drzava
{
public int IDDrzava;
public string NazivDrzave;

public Drzava() { }

public Drzava(int idDrzava, string nazivDrzave)
{
IDDrzava = idDrzava;
NazivDrzave = nazivDrzave;
}

}

Anonymous said...

android side

public class ArrayOfObjectsCaller {

final static String METHOD_NAME = "GetAllDrzavaByArray";
final static String SOAP_ACTION = "http://matija.org/GetAllDrzavaByArray";
public static final String NAMESPACE = "http://matija.org/";
public static final String url = "http://10.0.2.2:43849/SQLKomunikacija/Service.asmx";

public static Drzava[] GetAllDrzava() {
SoapObject response = InvokeMethod(url, METHOD_NAME);
return RetrieveFromSoap(response);
}

public static SoapObject InvokeMethod(String URL, String MethodName) {
SoapObject request = GetSoapObject(MethodName);
SoapSerializationEnvelope envelope = GetEnvelope(request);
return MakeCall(URL, envelope, NAMESPACE, MethodName);
}

public static SoapObject GetSoapObject(String MethodName) {
return new SoapObject(NAMESPACE, MethodName);
}

public static SoapSerializationEnvelope GetEnvelope(SoapObject Soap) {
SoapSerializationEnvelope envelope = new SoapSerializationEnvelope( SoapEnvelope.VER11);
envelope.dotNet = true;
envelope.setOutputSoapObject(Soap);
envelope.addMapping(NAMESPACE, "Drzava",new Drzava().getClass());
return envelope;
}

public static SoapObject MakeCall(String URL,SoapSerializationEnvelope Envelope, String NAMESPACE,String METHOD_NAME) {
HttpTransportSE androidHttpTransport = new HttpTransportSE(URL);
try {
androidHttpTransport.call(NAMESPACE + METHOD_NAME, Envelope);
SoapObject response = (SoapObject) Envelope.bodyIn;
return response;
} catch (Exception e) {
e.printStackTrace();

}
return null;
}

public static Drzava[] RetrieveFromSoap(SoapObject soap) {
SoapObject ObjSoap = (SoapObject)soap.getProperty(0);
Drzava[] drzave = new Drzava[Integer.parseInt(ObjSoap.getProperty(0).toString())];
for (int i = 0; i < drzave.length; i++) {
SoapObject pii = (SoapObject)soap.getProperty(i);
Drzava dr = new Drzava();
dr.IDDrzava = Integer.parseInt(pii.getProperty(0).toString());
dr.NazivDrzave = pii.getProperty(1).toString();

drzave[i] = dr;
}
return drzave;
}

}

public class Drzava implements KvmSerializable {
public int IDDrzava;
public String NazivDrzave;

public Drzava(){}

public Drzava(int iDDrzava, String nazivDrzave)
{
IDDrzava = iDDrzava;
NazivDrzave=nazivDrzave;
}

public Object getProperty(int arg0) {
switch(arg0)
{
case 0:
return IDDrzava;
case 1:
return NazivDrzave;
}
return null;
}

public int getPropertyCount() {
return 2;
}

public void getPropertyInfo(int index, Hashtable hashtable, PropertyInfo info) {
switch(index)
{
case 0:
info.type = PropertyInfo.INTEGER_CLASS;
info.name="IDDrzava";
break;
case 1:
info.type = PropertyInfo.STRING_CLASS;
info.name = "NazivDrzave";
break;
default:break;
}

}

public void setProperty(int index, Object value) {
switch(index)
{
case 0:
IDDrzava = Integer.parseInt(value.toString());
break;
case 1:
NazivDrzave = value.toString();
break;
default:
break;
}

}

}
and this method iterate through soap response and set each object in textview
public void prikaziDrzave() {
et = (EditText) findViewById(R.id.editText3);
Drzava[] dr = ArrayOfObjectsCaller.GetAllDrzava();

for (int i = 0; i < dr.length; i++) {
Drzava d = (Drzava) dr[i];
tekst = "IDDrzava: " + d.IDDrzava + " ,Naziv " + d.NazivDrzave + "\n";
}
et.setText(tekst);
}

SeeSharpWriter said...

Hello! In the .NET side, I don't see where your listaDrzava variable is defined. You probably wanted to write something like:

List listaDrzava = new List();

SeeSharpWriter said...

List<Drzava> listDrzava = new List<Drzava>()

Anonymous said...

i updated the code so i copied only changed parts
.net side
[Serializable]
public class Drzava
{
public int IDDrzava;
public string NazivDrzave;
public static List listaDrzava = new List(); // i added this line like you said
public Drzava() { }

public Drzava(int idDrzava, string nazivDrzave)
{
IDDrzava = idDrzava;
NazivDrzave = nazivDrzave;
}

}

[WebMethod]
public Drzava[] GetAllDrzavaByArray()
{
string connCS = ConfigurationManager.ConnectionStrings["mojCS"].ConnectionString;
Drzava dr = null;

SqlDataAdapter adapter = new SqlDataAdapter();
using (SqlConnection conn = new SqlConnection(connCS))
{
using (SqlCommand cmd = conn.CreateCommand())
{
cmd.CommandText = "prikazi";
cmd.CommandType = CommandType.StoredProcedure;

conn.Open();

using (SqlDataReader reader = cmd.ExecuteReader())
{
while (reader.Read())
{
dr = new Drzava();
dr.IDDrzava = (int)reader["IDDrzava"];
dr.NazivDrzave = (string)reader["NazivDrzave"];
Drzava.listaDrzava.Add(dr);// i changed this line
}
}
}
}
return Drzava.listaDrzava.ToArray(); // changed this line also

}

from android side
public static SoapObject MakeCall(String URL,SoapSerializationEnvelope Envelope, String NAMESPACE,String METHOD_NAME) {
HttpTransportSE androidHttpTransport = new HttpTransportSE(URL);
try {
androidHttpTransport.call(NAMESPACE + METHOD_NAME, Envelope);
SoapObject response = (SoapObject) Envelope.getResponse(); // changed this line because otherwise gives me NumberFormatException
return response;
} catch (Exception e) {
e.printStackTrace();

}
return null;
}

and is the same problem again (returns only one entry). I don't know why because when i invoke webmethod from service it returns all data. Can you please see and give me in details what is wrong with my code.I really don't know what to do anymore I also using ksoap2-android-assembly-2.4-jar-with-dependencies
Thanx in advance

Anonymous said...

from .net side i changed line
public static List listaDrzava = new List(); //

into
public static List listaDrzava = new List();

and the same problem again

Anonymous said...

List listaDrzava = new List ()

SeeSharpWriter said...

I meant.. make the list a local variable in your web method, not a static one in the class...

SeeSharpWriter said...

It is also a good idea to debug the android code and see what is inside the response object, whether all objects are received. Maybe they are wrapped differently.

Anonymous said...

List%lt;Drzava> listaDrzava = new List<Drzava> ()

Anonymous said...

hey can you write a full source code from .net and android side about passing array of objects using ksoap, when you got a lot time.
i will apreciate it.

tcastro said...

Hi, im new to android dev and im having some trouble retriving the data from a webservice.
this is the xml that webservice returns:





int
string
short
unsignedByte


int
string
short
unsignedByte


<_erro>
unsignedByte
string




and from the response i get some like:
anyType{
aServicos=anyType{
Servico=anyType{idPMT=262; descServico=BO UROLOGIA; ano=2011; mes=10; };
Servico=anyType{idPMT=263; descServico=CIRURGIA GERAL; ano=2011; mes=11; };
Servico=anyType{idPMT=267; descServico=BO UROLOGIA; ano=2011; mes=11; }; }; _erro=anyType{erro=0; }; }


i thinking about doing some string splits , but i dont think that its a good idea


can you give me some advice how to get the an array of "Servico".

sorry for my english
hope hearing from u soon

SeeSharpWriter said...

You should create a class on the Android's side that implements KVMSerializable interface and then use a for loop to map the response to a list of your objects:

public static Category[] RetrieveFromSoap(SoapObject soap)
{
Category[] categories = new Category[soap.getPropertyCount()];
for (int i = 0; i < categories.length; i++) {
SoapObject pii = (SoapObject)soap.getProperty(i);
Category category = new Category();
category.CategoryId = Integer.parseInt(pii.getProperty(0).toString());
category.Name = pii.getProperty(1).toString();
category.Description = pii.getProperty(2).toString();
categories[i] = category;
}
return categories;
}

In your case I assume it will be Servico instead of Category.

tcastro said...

-GetServicosResult>
-- aServicos>
--- Servico>
---- idPMT>int
---- descServico>string
---- ano>short
---- mes>unsignedByte
--- /Servico>
etc...


sry for the above i was pasting xml code

SeeSharpWriter said...

You should not have any problems with int,string,short,unsignedByte data types.

tcastro said...

thx for the help i could manage my self :)... hmm one more question if i have something like this -> | anyType{aTurnos=anyType{Turno=anyType{idTurno=169; sigla=M1; descTurno=Manhã 1; horaInicio=07:30; horaFim=13:00; horasExtra=false; idHoraExtraTipo=0; greveServicosMinimos=false; aSectores=anyType{Sector=anyType{descSector=Sala 1; horaInicio=07:30; horaFim=13:00; }; }; }; }; _erro=anyType{erro=0; }; } |

and i want to create this array

aSectores=anyType{Sector=anyType{descSector=Sala 1; horaInicio=07:30; horaFim=13:00; };

i would appreciate your help

tcastro said...

i manage to solve my problem :) thx anyway

SeeSharpWriter said...

Hello tcastro! Could you please share your solution? I am sure somebody will find it useful. Thanks!

Pedro said...

PART ONE/TWO

Hi guys,

I have been struggling with this for a few days, and I have read & re-read all of ksoap2 related threads I could find. Thanks SeeSharpWriter and all the contributors/suggestions.
I'd like to "share back" my working example. Don't evaluate the code, I'm sure you can do better. It's a proof-of-concept only.
I'm using ksoap 2.6.0 lib.
I have a C# webservice that returns an array of Person. It receives a dummy string that is appended to each person's name.
The service creates 16 persons and returns them as an array Person[]
The client opens the envelope and creates locally 16 MyPerson by inspecting each of the response's properties.
I hope it works for you. I cannot provide much support, unfortunately. My motivation is to encourage the usage/development of such libraries which, IMHO, need to improve on documentation and tool-generated stubs.

--- WEBSERVICE ---
namespace xxx {
public class Person {
public string Name;
public string EmailID;
public string Gender;
public Person() { }
public Person(string pName, string pEmailID,string pGender) {
Name = pName;
EmailID = pEmailID;
Gender = pGender;
}
}
}
namespace xxx {
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem(false)]
public class lookupws : System.Web.Services.WebService {
[WebMethod]
public string Millis() {
return "OK @ " + System.DateTime.Now.Millisecond.ToString() ;
}
[WebMethod]
public string Echo(String p) {
return "OK. Received: >" + p + "<";
}
[WebMethod]
public Person[] getPersonList(string crit) {
Person[] lp = new Person[16];
for (int i = 0; i < lp.Length; i++) {
lp[i] = new Person("name" + i + crit,"email" + i, ( i % 2 == 0 ? "M" : "F") );
}

return lp;
}
}
}

Pedro said...

PART TWO / TWO
--- ANDROID CLIENT ---
-- START MyPerson.java
package xxx;
import java.util.Hashtable;
import org.ksoap2.serialization.KvmSerializable;
import org.ksoap2.serialization.PropertyInfo;
public class MyPerson implements KvmSerializable {
public String Name;
public String EmailID;
public String Gender;
public MyPerson(){}
public MyPerson(String pName,String pEmailID, String pGender ){
Name = pName;
EmailID = pEmailID;
Gender = pGender;
}
@Override
public Object getProperty(int arg0) {
switch(arg0)
{
case 0:
return Name;
case 1:
return EmailID;
case 2:
return Gender;
}
return null;
}
@Override
public int getPropertyCount() {
return 3;
}
@Override
public void getPropertyInfo(int index, @SuppressWarnings("rawtypes") Hashtable arg1, PropertyInfo info) {
switch(index)
{
case 0:
info.type = PropertyInfo.STRING_CLASS;
info.name = "Name";
break;
case 1:
info.type = PropertyInfo.STRING_CLASS;
info.name = "EmailID";
break;
case 2:
info.type = PropertyInfo.STRING_CLASS;
info.name = "Gender";
break;
default:break;
}
}
@Override
public void setProperty(int index, Object value) {
switch(index)
{
case 0:
Name = value.toString();// Integer.parseInt(value.toString());
break;
case 1:
EmailID = value.toString();
break;
case 2:
Gender = value.toString();
break;
default:
break;
}
}
}
-- END MyPerson.java

Pedro said...

PART THREE / AFTER-ALL-I-NEED-THREE-PARTS
-- START WS call method
protected void callPersonWS_TWO(String p2) {
SoapObject request = new SoapObject(
((EditText) findViewById(R.id.editText_PersonNamespace)).getText().toString() // NAMESPACE
, ((EditText) findViewById(R.id.editText_PersonMethodName)).getText().toString() // METHOD_NAME
);
SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);
PropertyInfo argumentProp =new PropertyInfo();
argumentProp.setName("crit");
argumentProp.setValue(p2);
argumentProp.setType(String.class);
request.addProperty(argumentProp);
envelope.dotNet = true;
envelope.setOutputSoapObject(request);
HttpTransportSE androidHttpTransport = new HttpTransportSE(
((EditText) findViewById(R.id.editText_PersonURL)).getText().toString() // URL
);
try {
envelope.addMapping(
((EditText) findViewById(R.id.editText_PersonNamespace)).getText().toString()
, "Person",new MyPerson().getClass());
androidHttpTransport.call(
((EditText) findViewById(R.id.editText_PersonSoapAction)).getText().toString()
, envelope);
SoapObject response = (SoapObject)envelope.getResponse();
MyPerson[] personList = new MyPerson[response.getPropertyCount()];
for (int j = 0; j < personList.length; j++) {
SoapObject pii = (SoapObject)response.getProperty(j);
MyPerson person = new MyPerson();
person.Name = pii.getProperty(0).toString();
person.EmailID = pii.getProperty(1).toString();
person.Gender = pii.getProperty(2).toString();
personList[j] = person;
}
AlertManager.showToast(getApplicationContext(), personList.length + " persons returned");
for (int i = 0; i < personList.length; i++) {
Log.d("SNPS", "[WSPOCActivity.callPersonWS_TWO()] " + personList[i].Name + " " + personList[i].EmailID + " " + personList[i].Gender );
}
} catch (IOException e) {
e.printStackTrace();
} catch (XmlPullParserException e) {
e.printStackTrace();
}
}
-- END WS call method

SeeSharpWriter said...

Thank you Pedro for your full code contribution! Many people have been asking for a full code so your deed is highly appreciated. I am sure you will save many nerves of developers worldwide.
It is a bit odd that such a simple task is so hard to implement in a mobile environment such as Android.

Anonymous said...

HI can you help me in parsing this response


service response = anyType{errorCode=-1; errorInfo=anyType{};
facilities=anyType{FacilityInfo=anyType{facilityID=1; cardTrack=0; creationDate=9/27/2011 4:50:11 PM; dstTypeID=1;
dstAheadMonth=3; dstAheadSunday=2; dstBackMonth=11; dstBackSunday=0; idCodeLength=4; name=Tech Support; pinLength=3;
referenceName=anyType{}; signOnKey=695900; }; FacilityInfo=anyType{facilityID=2; cardTrack=1; creationDate=2/27/2012
9:30:34 PM; dstTypeID=1; dstAheadMonth=3; dstAheadSunday=2; dstBackMonth=11; dstBackSunday=0; idCodeLength=4; name=New
Facility; pinLength=4; referenceName=New Reference; signOnKey=123457; }; FacilityInfo=anyType{facilityID=3;
cardTrack=1; creationDate=2/27/2012 9:31:50 PM; dstTypeID=2; dstAheadMonth=3; dstAheadSunday=1; dstBackMonth=10;
dstBackSunday=1; idCodeLength=4; name=IT Support; pinLength=3; referenceName=IT Reference; signOnKey=123789; }; }; }

Pedro said...

Hi Anonymous,
Don't "parse" the response. Create a client side object and inspect it's properties.

I've made some changes to the example I posted before. As you may have read, my web service returns a Person[]. Each Person object has the following fields:

public string Name;
public string EmailID;
public string Gender;
public string Photo;
public Contact[] Contacts;

and Contact has the following fields:

public string type;
public string action;
public string value;

Now the challenge is to un-envelope such response.

I'll post what I did in a few minutes.

Pedro

Pedro said...

(continued)

Taking my previous example as the background, this is the main block where Contact[] is extracted from the envelope.

Hope if floats your boat.

Pedro

-- BLOCK START

MyPerson[] personList = new MyPerson[response.getPropertyCount()];

for (int j = 0; j < personList.length; j++) {

SoapObject pii = (SoapObject)response.getProperty(j);

MyPerson person = new MyPerson();

person.Name = pii.getProperty(0).toString();
person.EmailID = pii.getProperty(1).toString();
person.Gender = pii.getProperty(2).toString();
person.Photo = pii.getProperty(3).toString();

Contact[] personContacts = new Contact[((SoapObject)pii.getProperty(4)).getPropertyCount()];

for (int i = 0; i < personContacts.length; i++) {

Contact contact = new Contact();

contact.type = ((SoapObject)((SoapObject)pii.getProperty(4)).getProperty(i)).getProperty(0).toString();
contact.action = ((SoapObject)((SoapObject)pii.getProperty(4)).getProperty(i)).getProperty(1).toString();
contact.value = ((SoapObject)((SoapObject)pii.getProperty(4)).getProperty(i)).getProperty(2).toString();

personContacts[i] = contact;

}

person.Contacts = personContacts;


personList[j] = person;

}

-- BLOCK END

Anonymous said...

Hi,

Is there any way to capture System.io.Stream object return from wcf service?

Pedro said...

Hi,
does it make sense for a web service to return a stream object?
In my opinion makes more sense for a web service to return a URL with a streaming endpoint, like
"Where can I stream from? Ask the web service".

I've been playing safe and avoiding maintenance chaos by using primitive types and arrays. (And contrarily to the original article, I have mapped Arrays to Vector without problem).

SeeSharpWriter said...

Hi Pedro,

could you please share a working example of using the VECTOR type to map an encapsulated array within a complex object?

Pedro said...

Hi SeeSharp,

please check the MyPerson class exemplified before.
On Android side, I have changed the getPropertyInfo method like this:

-- START

@Override
public void getPropertyInfo(int index, @SuppressWarnings("rawtypes") Hashtable arg1, PropertyInfo info) {
switch(index)
{
case 0:
info.type = PropertyInfo.STRING_CLASS;
info.name = "Name";
break;
case 1:
info.type = PropertyInfo.STRING_CLASS;
info.name = "EmailID";
break;
case 2:
info.type = PropertyInfo.STRING_CLASS;
info.name = "Gender";
break;
case 3:
info.type = PropertyInfo.STRING_CLASS;
info.name = "Photo";
break;
case 4:
info.type = PropertyInfo.VECTOR_CLASS;
info.name = "Contacts";
break;
default:break;
}

}

-- END

It works fine.

Anonymous said...

Hi, can you please show me example how to insert new data from android to sql server. What do i need to do from web service to get the data and manipulate with it.

Anonymous said...

i mean something like crud operations from android to .net web service

Pedro said...

Hi Anonymous,
That falls way beyond the scope of SOAP based web services using ksoap2 lib. The code I have would be too complex to post here, so I suggest googling for CRUD operations using .net and sql server.

What you are requesting is strictly server-sided, being the actions triggered within the [WebMethods] your service exposes.

neo3 said...

@SeeSharpWriter

Hi,

could you please contact me at my email adress: n30dr31@googlemail.com?

I want to refer to your article in my master thesis. So it would look much more serious, if you told me your real name ^^

If you have any questions, just ask me and I'll verify my intentions!

greetings from Germany,
neo3

sumati said...

@SeeSharpWriter

Hi.great article

Iam tying to develope an app which needs to insert image in binary(base64string) into the remote databse(sql server) by consuming web services written in c#.i hav followed ur article..but stuck up with this exception which says:
01-02 16:42:34.685: E/SoapError :(17899): Error System.Web.Services.Protocols.SoapException: Server was unable to process request. ---> System.ArgumentNullException: Value cannot be null.
01-02 16:42:34.685: E/SoapError :(17899): Parameter name: InString
01-02 16:42:34.685: E/SoapError :(17899): at System.Convert.FromBase64String(String s)
01-02 16:42:34.685: E/SoapError :(17899): at WebServiceDemo.InsertImage(String name) in c:\Documents and Settings\admin\My Documents\Visual Studio 2008\WebSites\Webservicedemo\webservicedemo.asmx:line 164
01-02 16:42:34.685: E/SoapError :(17899): --- End of inner exception stack trace ---



iam passing a base64string as parameter.the webservice code is right..seems its not recieving...and no insertion performed.really stuck up with this since long....plsss suggest.


any help would be appreciated
thanx

theman said...

Thank you for your post.

lanquansan said...

Hi u!
Can u help me?

With tutorial this,what's data return of method Web service ? and what's language of Web service?

Thanks!

Guillermo said...

Excellent post buddy. Many thanks!

SeeSharpWriter said...

I am glad you found this tutorial useful, Guillermo!

Anonymous said...

Hey I have a class which returns an array I would like to pass it to the android project class which contains the ksoap functions then pass it to the web service

Anonymous said...

Hi everyone
this is very userful tutorial thanks so much.
i want know if there is difference between calling web service from activity and simple classe?

Anonymous said...

when am clicking in an item name i need to get all its details...can u provide me with the complete code for doing so..we need to parse it using array..hw to use web service and how to parse them using array.....am new to android..

Anonymous said...

Would you be willing to release a working project for this? I have been beating my head against the wall for two days...would be much appreciated...

Anonymous said...

Hi,very useful guide and it worked for me.But what happens if i have as a return an array of objects (just like your example)but each object has an inner object as property.How can i then parse my response?

SeeSharpWriter said...

That is something I have never managed to achieve.However, I think that someone in the comments mentioned that was able to accomplish. My advice would be that you go through the comments, you might find something useful for that case.

Anonymous said...

Hi

I get a nullpointer exception and the following stack trace:

The message with Action 'http://tempuri.org/getAll' cannot be processed at the receiver, due to a ContractFilter mismatch at the EndpointDispatcher. This may be because of either a contract mismatch (mismatched Actions between sender and receiver) or a binding/security mismatch between the sender and the receiver. Check that sender and receiver have the same contract and the same binding (including security requirements, e.g. Message, Transport, None).

SeeSharpWriter said...

It looks like you have a WCF service. You better check your configuration of the service as well as whether the class is the same in Android and .NET.

Another thing you could try is to make it work with .asmx web service first, and then work with WCF.

Anonymous said...

This is my class in WCF:

[DataContract(IsReference = true)]
public class Category
{
public int _categoryId;
public String _name;
public String _description;

public Category(){}


public Category(int categoryId, String name, String description) {

_categoryId = categoryId;
_name = name;
_description = description;
}

[DataMember]
public int CategoryId
{
get { return _categoryId; }
set { _categoryId = value; }
}

[DataMember]
public string Name
{
get { return _name; }
set { _name = value; }
}

[DataMember]
public string Description
{
get { return _description; }
set { _description = value; }
}

}

}

and this is my webconfig:



























And my IService interface:

[OperationContract]
Category[] getAll();

Can you spot a problem?

Anonymous said...

web config:

//configuration>
system.serviceModel>
services>
service behaviorConfiguration="CalcDistance.Service1Behavior"
name="CalcDistance.Service1">
endpoint address="" binding="basicHttpBinding" contract="CalcDistance.IService1">
identity>
dns value="localhost" />
/identity>
/endpoint>
endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
/service>
/services>
behaviors>
serviceBehaviors>
behavior name="CalcDistance.Service1Behavior">

serviceMetadata httpGetEnabled="true"/>

serviceDebug includeExceptionDetailInFaults="true"/>
/behavior>
/serviceBehaviors>
/behaviors>
/system.serviceModel>
/configuration>

_alex_ said...

Each time I upload a new Object it commits to database an empty object and ignores all parameters I set to it.

Here is android side:
public void createGroupServ(String groupName)
{
request = new SoapObject(NAMESPACE, "createGroup");

Group gr = new Group();
gr.setGroupId(1L);
gr.setGroupName("xxxx");
gr.setUsers(null);

PropertyInfo object = new PropertyInfo();
object.setName("arg0");
object.setValue(gr);
object.setType(gr.getClass());
request.addProperty(object);

envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);
envelope.dotNet = false;
envelope.setOutputSoapObject(request);
envelope.addMapping(NAMESPACE, "group", new Group().getClass());

androidHttpTransport = new HttpTransportSE(URL);
androidHttpTransport.debug = true;

}

and domain class Group:
public class Group implements KvmSerializable {
private long groupId;
private String groupName;
private List users = null;

...setter and getters ...
}

Any advice on why it doesn't upload the whole object with group name parameter ?

_alex_ said...

ok, fixed that, haven't implemented serializebable interface methods.

Anonymous said...

Hey! First of all, great tutorial. It's been a couple of days I've had some issues receiving an array of complex objects. I tried many different implementations, but I consistently get the same error (RuntimeException: unknown property poi, since I'm trying to receive an array of 'poi' complex objects). This error is launched in the HttpTransportSE::call() and of course I previouly added the mapping of this object in the envelope. Anyway, my structure is a bit different than yours, since the array is not the root object of my response. What do you think, I'm running out of ideas! Thanks in advance.

SeeSharpWriter said...

Hi! Why don't you paste some code of the class you are trying to transfer and the way you are trying to invoke the service?

Anonymous said...

Of course! I'll break this answer in 2 pieces, since the maximum seems to be 4096 characters.

Let's see the most relevant parts:

snippet where the call is made:

[Class: GetPOIList.java]

POIListRQ rq = new POIListRQ(c, l, s, "");

PropertyInfo pi = new PropertyInfo();
pi.setName("request");
pi.setValue(rq);
pi.setType(rq.getClass());
request.addProperty(pi);

SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(
SoapEnvelope.VER11);
envelope.dotNet = false;
envelope.setOutputSoapObject(request);

envelope.avoidExceptionForUnknownProperty = false;

envelope.addMapping(Params.NAMESPACE, "POIListRQ", POIListRQ.class);
envelope.addMapping(Params.NAMESPACE, "POIListRS", POIListRS.class);
envelope.addMapping(Params.NAMESPACE, "Configuration", Configuration.class);
envelope.addMapping(Params.NAMESPACE, "Location", Location.class);
envelope.addMapping(Params.NAMESPACE, "POISearch", POISearch.class);
envelope.addMapping(Params.NAMESPACE, "Type", Type.class);
envelope.addMapping(Params.NAMESPACE, "POIResults", POIResults.class);
envelope.addMapping(Params.NAMESPACE, "POIBasicInfo", POIBasicInfo.class);
//envelope.addMapping(Params.NAMESPACE, "poi", POIBasicInfo.class);

MarshalDouble md = new MarshalDouble();
md.register(envelope);

HttpTransportSE htse = new HttpTransportSE(Params.URL);
htse.debug = true;

try {
htse.call(Params.GetPOIList.SOAP_ACTION, envelope);
// SoapObject response = (SoapObject) envelope.getResponse();
SoapObject response = (SoapObject) envelope.bodyIn;
return parseResponse(response);
} catch (Exception e) {
Log.d("SOAP", "Request: " + htse.requestDump);
Log.d("SOAP", "Response: " + htse.responseDump);
success = false;
e.printStackTrace();
}

note the avoidExceptionForUnknownProperty property set to false. That error can be 'avoided' if it is set to true, but it is not the appropiate solution, since it would simply ignore the 'poi' type.
===============================================
Class definition of the object array (not the elements). It extends Vector as explained, for example, here http://code.google.com/p/ksoap2-android/wiki/CodingTipsAndTricks#sending/receiving_array_of_complex_types_or_primitives :

[Class: POIResults.java]

package com.prod.android.net.type;

import java.util.Hashtable;
import java.util.Vector;

import org.ksoap2.serialization.KvmSerializable;
import org.ksoap2.serialization.PropertyInfo;

import com.prod.android.net.Params;

public class POIResults extends Vector implements KvmSerializable
{

public POIResults(){}

@Override
public Object getProperty(int index) {
return this.get(index);
}

@Override
public int getPropertyCount() {
return this.size();
}

@Override
public void getPropertyInfo(int index, Hashtable ht, PropertyInfo info) {
info.name = "poi";
info.type = POIBasicInfo.class;
info.namespace = Params.NAMESPACE;
}

@Override
public void setProperty(int index, Object value) {
this.add((POIBasicInfo) value);
}

}

Anonymous said...

Class definition of the array elements (the 'poi' objects):

[Class: POIBasicInfo.java]

package com.prod.android.net.type;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Hashtable;

import org.ksoap2.serialization.KvmSerializable;
import org.ksoap2.serialization.PropertyInfo;

import android.os.Parcel;
import android.os.Parcelable;

public class POIBasicInfo implements KvmSerializable
{

public int code;
public int priority;
public String name;
public String address;
public String thumbnail;
public Location location;
public Type type;
public double distance;

public POIBasicInfo(){}

public POIBasicInfo(int _code, int _priority, String _name,
String _address, String _thumbnail, Location _location,
Type _type, double _distance) {
this.code = _code;
this.priority = _priority;
this.name = _name;
this.address = _address;
this.thumbnail = _thumbnail;
this.location = _location;
this.type = _type;
this.distance = _distance;
}

@Override
public Object getProperty(int index) {

switch (index) {
case 0:
return this.code;
case 1:
return this.priority;
case 2:
return this.name;
case 3:
return this.address;
case 4:
return this.thumbnail;
case 5:
return this.location;
case 6:
return this.type;
case 7:
return this.distance;
}

return null;
}

@Override
public int getPropertyCount() {
return 8;
}

@Override
public void getPropertyInfo(int index, Hashtable ht, PropertyInfo info) {

switch (index) {
case 0:
info.type = PropertyInfo.INTEGER_CLASS;
info.name = "code";
break;
case 1:
info.type = PropertyInfo.INTEGER_CLASS;
info.name = "priority";
break;
case 2:
info.type = PropertyInfo.STRING_CLASS;
info.name = "name";
break;
case 3:
info.type = PropertyInfo.STRING_CLASS;
info.name = "address";
break;
case 4:
info.type = PropertyInfo.STRING_CLASS;
info.name = "thumbnail";
break;
case 5:
info.type = Location.class;
info.name = "location";
break;
case 6:
info.type = Type.class;
info.name = "type";
case 7:
info.type = Double.class;
info.name = "distance";
break;
default:
break;
}

}

@Override
public void setProperty(int index, Object value) {

switch (index) {
case 0:
code = Integer.parseInt(value.toString());
break;
case 1:
priority = Integer.parseInt(value.toString());
break;
case 2:
name = value.toString();
break;
case 3:
address = value.toString();
break;
case 4:
thumbnail = value.toString();
break;
case 5:
location = (Location) value;
break;
case 6:
type = (Type) value;
break;
case 7:
distance = Double.parseDouble(value.toString());
break;
default:
break;
}

}

}
And well, checking the response etc, the namespace seems correct, since it is the same as in the other complex objects.

Anonymous said...

Of course, poi is the name of every element in the array, but the object type is defined to be 'POIBasicInfo'

SeeSharpWriter said...

Oh that's cool that someone has finally explained how to work with arrays and vectors!
As far as I can remember, you dont necessarily need extending vector class to receive an array of objects from the web service, but would be delighted to see a working example,


Another question though, do you own both the Android code and the Web Service code?

Anonymous said...

Mmm... I'm using the 'extends Vector' method because the ksoap2 Vector won't work for me :( But then, if I don't need to extend it, how can I receive the array? I tried your approach but I couldn't make it work either. Maybe because we have a different structure, but I'm not sure...

And yes, I own both sides, Android & Web service. Need to post it? It's a normal SoapServer in PHP

SeeSharpWriter said...

I was thinking to propose a much simpler idea.. At least this is how I would do it if I had to code it today, almost 3 years later...

Don't use KSOAP! Don't use SOAP at all.. why dont you make your service return simple XML structure which you can then parse on the Android device and translate it to business objects?

Trust me, you will have much less headaches with this approach.

Anonymous said...

It seems that the blog doesnt parse correctly the 'html' tags, so I will tell you that the extended Vector in POIResults is parameterized to POIBasicInfo (it extends a Vector of POIBasicInfo, not a normal Vector)

Anonymous said...

Believe me, I have thought of it, many times! But I'm a bit on a hurry, but anyway, do you think it would take much time to make the translation?

SeeSharpWriter said...

Yeah for some reason Blogger interprets them instead of displaying them ... :/ . You can do Ctrl + H and replace them with entities &lt; and &gt;

Anonymous said...

Hi, I am getting output like this GetProductDetailsResponse{return=[Products{ID=29; ProductName=Product1; ImageUrl=Product1.jpg; }, Products{ID=30; ProductName=Product2; ImageUrl=Product2.jpg; }]}; from response.toString();
I have used SoapObject response = (SoapObject) envelope.bodyIn; because when I use SoapObject response = (SoapObject) envelope.getResponse();
It gives vector class cast exception I am new to soap, I have created this webservice by my own in php. Please help..

SeeSharpWriter said...

If you are using PHP, why do you strictly need SOAP service? Why don't you return a simple XML list and parse it on the Android device?

Anonymous said...

I have already done that.. I would love too, but boss is boss... I have to do this in soap. I am using nusoap currently...near to exact example of what I have done is can be found in this link http://www.discorganized.com/category/php/

SeeSharpWriter said...

Are you attempting to use the Vector class from KSOAP?

My code implementation avoids using it and works with array of complex objects.

Anonymous said...

Sorry, but I don't know anything about vector class from KSOAP, I can I find that? and remove that? I have checked your code and implement in my code. I am getting class cast exception

SeeSharpWriter said...

You will need to inspect the KSOAP library for that..

Make sure that you follow all my articles on the topic. See how the class Category is implemented here:

http://seesharpgears.blogspot.com/2010/10/ksoap-android-web-service-tutorial-with.html

It would be an equivalent of your Product class.

Also it would be useful if you validate your SOAP to make sure it is not causing any trouble.

Another thing to check is this link: http://code.google.com/p/ksoap2-android/wiki/CodingTipsAndTricks

Anonymous said...

I found that, I am getting whole response of soap as one object, means getting whole detail in getProperty(0). what it looks like? fault from android or php code?

SeeSharpWriter said...

It looks to me like you need to modify the Android code, not the PHP code. If I were you, I would validated my SOAP output and then debug the Android code .

Manmohan said...

SeeSharpWriter, thx for valuable info.
why you dont put a working source code to download?
we met many errors and bugs.

please provide a working source code ( for android 4+) specially for this article

thx for your effort

大宝 said...
This comment has been removed by the author.
大宝 said...
This comment has been removed by the author.
David said...

Hello, i have tried the send a complex type( List) but it not can serialize this object.
The web service is ok, I used with jax-ws and this work correctly.
////////////////////////////////////////////////////////////////////////////////////////
Class Args whit jax-ws:
/**
* Java class for anonymous complex type.
* The following schema fragment specifies the expected content contained within this class.
*
* <complexType>
* <complexContent>
* <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
* <sequence>
* <element name="item" type="{http://www.w3.org/2001/XMLSchema}string" maxOccurs="unbounded" minOccurs="0"/>
* </sequence>
* </restriction>
* </complexContent>
* </complexType>
*/
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
"item"
})
@XmlRootElement(name = "args")
public class Args {

protected List item;

public List getItem() {
if (item == null) {
item = new ArrayList();
}
return this.item;
}
}
////////////////////////////////////////////////////////////////////////////////////////
Class whit android:
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;

import org.ksoap2.serialization.KvmSerializable;
import org.ksoap2.serialization.PropertyInfo;

public class Args implements KvmSerializable {

public List item;

public List getItem() {
if (item == null) {
item = new ArrayList();
}
return this.item;
}

@Override
public Object getProperty(int arg0) {
switch (arg0) {
case 0:
return item;
}
return null;
}

@Override
public int getPropertyCount() {
return 1;
}

@Override
public void getPropertyInfo(int index, Hashtable arg1, PropertyInfo info) {
switch (index) {
case 0:
info.type = PropertyInfo.VECTOR_CLASS;
info.name = "item";
break;
default:
break;
}
}

@Override
public void setProperty(int index, Object value) {
switch (index) {
case 0:
item = (List) value;
break;
default:
break;
}
}
}

thanks for your help..

Shiva said...

As per your example I got null value of category object. can you help me?

package com.test;

import org.ksoap2.SoapEnvelope;
import org.ksoap2.serialization.PropertyInfo;
import org.ksoap2.serialization.SoapObject;
import org.ksoap2.serialization.SoapSerializationEnvelope;
import org.ksoap2.transport.HttpTransportSE;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.EditText;


public class TestWebServiceActivity extends Activity implements OnClickListener {
/** Called when the activity is first created. */

String NAMESPACE = "http://test.com";
String METHOD_NAME = "testmethod";
String SOAP_ACTION = "http://com.test/Test";
String URL = "http://172.24.60.10:8085/TestProperty/services/Test?wsdl";
EditText textName;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
findViewById(R.id.but).setOnClickListener(this);
}


@Override
public void onClick(View v) {
switch(v.getId())
{
case R.id.but:
textName=(EditText)findViewById(R.id.textpass);
String str=textName.getText().toString();


SoapObject Request = new SoapObject(NAMESPACE, METHOD_NAME);

Category C = new Category();
//C.setName(str);
C.Name=str;

Log.d("name ", str);

PropertyInfo pi = new PropertyInfo();
/*pi.setName("str");
pi.setValue(str);
pi.setType(String.class);*/

pi.setName("C");
pi.setValue(C);
pi.setType(C.getClass());
Request.addProperty(pi);

SoapSerializationEnvelope soapenv= new SoapSerializationEnvelope(SoapEnvelope.VER11);
soapenv.dotNet=true;
soapenv.setOutputSoapObject(Request);
soapenv.addMapping(NAMESPACE, "Category",new Category().getClass());
HttpTransportSE ht=new HttpTransportSE(URL);
try
{
Log.d("name1 ", C.Name);
ht.call(SOAP_ACTION, soapenv);
//SoapPrimitive response=(SoapPrimitive)soapenv.getResponse();
SoapObject response= (SoapObject) soapenv.getResponse();
//tv.setText(""+sp);

}
catch(Exception e)
{
e.printStackTrace();
}
}

}

}

WebServer:

package com.test;

public class Test {

public Category testmethod(Category C){

return C;
}

}

Post a Comment