Kevin Thomas Kevin Thomas - 29 days ago 5
Vb.net Question

Return an Array of Strings from an ASP.NET Web Service to an Android client using SOAP

I want to return an array of strings to my Android client and populate a ListView.
I am using the SOAP library (org.ksoap2.*) to invoke an ASP.NET web service.

Here is the code for the web service:

1. ASP Web Service

Imports ...
Imports System.Web.Services
Imports System.Web.Services.Protocols
Imports ...

<WebService(Namespace:="...")>_
<WebService(ConformsTo:=...)> _
<Global.Microsoft.VisualBasic.CompilerServices.DesignerGenerated()> _

Public Class EnquiryWS
Inherits System.Web.Services.WebService

' Web method
<WebMethod()> _
Public Function GetList() As String()
'Hardcoded list
Return New String() { "item1", "item2", "item3" }
End Function


I've tested the web service by accessing the asmx, there are no runtime errors.
I've also tested it with just a simple string, the web service returned the string to Android. Like this:

' Web method
<WebMethod()> _
Public Function GetString() As String
Return "My string."
End Function


2. Android Activity

Secondly, here is my Android code that is invoking the ASP.NET web service.

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

public class MainActivity extends AppCompatActivity {

private ArrayList<String> list;
private ListView listview;
private ArrayAdapter<String> adapter;

@Override
protected void onCreate(Bundle savedInstanceState) {
//...
new GetPersonList().execute("AsyncTask String");
//...
}

// Inner AsyncTask class
private class GetPersonList extends AsyncTask<String, Integer,String> {
private static final String SOAP_ACTION = "https://myNamespace/GetList";
private static final String METHOD_NAME = "GetList";
private static final String NAMESPACE = "https://myNamespace/";
private static final String URL =
"https://myIISsite/myASMXfile.asmx";

@Override
protected void onPreExecute() {
super.onPreExecute();
// onPreExecute stuff
}

@Override
protected String doInBackground(String... arg) {
String result = null;

SoapObject request = new SoapObject(NAMESPACE, METHOD_NAME);

//Create envelope
SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);

//Required for .net
envelope.dotNet = true;

//Set output SOAP object
envelope.setOutputSoapObject(request);

//Create HTTP call object
HttpTransportSE androidHttpTransport = new HttpTransportSE(URL);

try {
//Invoke web service
androidHttpTransport.call(SOAP_ACTION, envelope);

//Get the response
SoapPrimitive response = (SoapPrimitive) envelope.getResponse();
//Assign it to response to a static variable
result = response.toString();
} catch (Exception e) {
result = "error " + e.getMessage();
}

return result;
}

@Override
protected void onPostExecute(String result) {
System.out.println("Returned SOAP XML: " + result);
MyFunction(result);
}
}
}


MyFunction is a method that I created to do some additional work.

3. MyFunction

Here is MyFunction method code:

public void MyFunction(String s) {
// Add Webservice response to list
list.add(s);

// Set adapter
adapter = new ArrayAdapter<String>(MainActivity.this, android.R.layout.simple_list_item1, list);
listview.setAdapter(adapter);

}


The argument I pass to MyFunction is the SOAP response, I then add it to the list and set the adapter.

Okay, so the web service is returning an array of strings, but the overriden onPostExecute method is working with one string, if I declare the onPostExecute parameter as a Collection, it is obviously not overriding anymore.

This is the error that I am getting in logcat:

Return SOAP XML: error expected: START_TAG {http://schemas.xmlsoap.org/soap/envelope/}Envelope (position:START_TAG <html>@1:7 in java.io.InputStreamReader@4182d238)


Could anyone please advise?

Answer

I have found a solution. I am casting the response as a SoapObject and not as a SoapPrimitive, the reason for that is because a SoapPrimitive is for primitive data types, SoapObject supports composite data types. Therefore, I am casting my array as a SoapObject and not as a SoapPrimitive anymore.

I have removed the MyFunction() method, because I am setting the adapter in the onPostExecute() method by override run(). And finally, I have added a ProgressDialog, I am showing the ProgressDialog in the onPreExecute() method while it's processing, and then I am invoking dismiss() in the onPostExecute() method.

    private class GetPersonList extends AsyncTask<Void, Void, String> {

    private static final String SOAP_ACTION = "http://myNamespace/myMethod";
    private static final String METHOD_NAME = "myMethod";
    private static final String NAMESPACE = "http://myNamespace/";
    private static final String URL =
            "http://myURL/myAsmxFile.asmx";

    ProgressDialog progressDialog;

    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        progressDialog= ProgressDialog.show(MainActivity.this,
                "Wait",
                "Retrieving data",
                true
        );
    }

    @Override
    protected String doInBackground(Void... params) {
        String finalResult = null;

        //Create request object
        SoapObject request = new SoapObject(NAMESPACE, METHOD_NAME);

        //Create envelope to which we add our request object
        SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(
                SoapEnvelope.VER11);

        //Required for .net
        envelope.dotNet = true;

        //Add the request object to the envelope
        envelope.setOutputSoapObject(request);

        //Create HTTP call object
        HttpTransportSE androidHttpTransport = new HttpTransportSE(URL);

        try {
            //Invoke web service
            androidHttpTransport.call(SOAP_ACTION, envelope);

            // Get the response
            // Cast as SoapObject and not SoapPrimitive
            SoapObject response = (SoapObject) envelope.getResponse();

            //Assign it to response to a static variable
            finalResult = response.toString();

            // Now loop through the response (loop through the properties)
            // and add them to the list
            for(int i = 0; i < response.getPropertyCount(); i++) {
                list.add(response.getProperty(i).toString());
            }

        } catch (Exception e) {
            System.out.println("######## ERROR " + e.getMessage());
        }

        return finalResult;
    }

    @Override
    protected void onPostExecute(String str) {
        progressDialog.dismiss();
        System.out.println("Returned SOAP XML: " + str);

        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                // Set adapter
                adapter = new ArrayAdapter<String>(MainActivity.this, R.layout.list_item, R.id.product_name, list);

                listview.setAdapter(adapter);
            }
        });
    }
}

I have heard that there is another way of doing this using Gson/Json, I will post that once I have figured it out.

Cheerz