KSOAP2 patch

The current version of kSOAP2, the Android library for communication via SOAP, does not offer facilities for serializing and deserializing complex objects. Furthermore, arrays are not supported.

In this post, we will introduce a patch of kSOAP2 which enables Android clients to communicate complex objects and arrays via SOAP. We will do that with the help of an example.

Here you can find:

  • the jar of our patch
  • the sources of the patched files
  • the sources of the example (both client and server)
  • the sources of the official version of kSOAP2.

To show how to use the patched library, let us consider a simple Soap Server, created thorugh Axis2 1.5, which stores information about students and exams. A student has some personal information (name, address, telephone number…), a lists of exams, and a tutor professor. As we can see from the server wsdl, the server publishes a method “put” with parameter “studente” of type “Studente”, and returns a result of type “int”, which is the newly created student identifier.

Step 1: how to create complex objects

To communicate with the server, first of all we have to create java classes for every complex object sent or received throught the Soap message.

So let’s start creating the object “Studente”, needed by the method “put” of the server. You can find the complete source of class Studente here.

Each complex object which has to be serialized or deserialized must be in a class which extends org.ksoap2.serialization.DataSoapObject (this has been created for the patch).

public class Studente extends DataSoapObject { ...

Throught reflection, DataSoapObject will write its public field, into SoapObject properties vector. For this reason, public fields of the complex object must be all the ones to be sent to the server via SoapMessage.

For instance, in order to create the class for Studente, we must inspect the wsdl of the server and insert every required field into Studente class:

public int id_student = 0;
public String nome = null;
public String cognome = null;
public String indirizzo = null;
public String telefono = null;
public Professore tutor = null;
public ListaEsami listaEsami = null;

As a subclass of DataSoapObject, Studente must redefine initMapping, in order to specify the mapping between itself and the name which will be found on the wsdl (which in our case is Studente) like this:

public static void initMapping() {
System.setProperty("Studente", "com.android.activitytest.data.Studente");
}

We must remember to call initMapping before even tring to serialize or deserialize Studente class and we must remember to call initMapping for every class we will use. A custom place to call it, is in the method OnCreate of the main activity, like this :

protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Esame.initMapping();
ListaEsami.initMapping();
Professore.initMapping();
Prova.initMapping();
Studente.initMapping();...

We must provide a constructor for Studente with arguments namespace and wsdl name, like this:

public Studente() {
super("http://test.axis2.com/xsd","Studente");
}

and that’s all. We can enrich Studente class with getter and setter for every field, with others constructor if needed, with a toStringcreateDefaultStudente method to have a student ready for test purpose.

What has been done for Studente, has to be done for every class in wsdl, i.e. Esame, ListaEsami, Professore, Prova.

WARNING: to be correctly deserialized, every complex object must contain at most one plain array of objects.

This is because every array item has the same property name, which is equals to SoapSerializationEnvelope.ARRAY_ITEM_LIST and because writing arrays in soap-xml code, DO NOT put a wrapper object around them.

Step 2: performing a call to the server

Performing a call to the server is done in the standard way; i didn’t need to change anything in its use, but be sure to be using my SoapSerializationEnvelope class, because it contains code to support array serialization.

As usual, we prepare the transport with URL, namespace and name method we want to call:

HttpTransportSE androidHttpTransport =
new HttpTransportSE(USED_URL);
SoapObject request =
new SoapObject("http://test.axis2.com", "put");

We create a student to be sent to the server and we add it to the request, using the name of the parameter of the method put, specified in wsdl file:

Studente studente =
Studente.createDefaultStudente("http://test.axis2.com");
request.addProperty("studente", studente);

We create an envelope and we insert the request into it:

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

Then, we perform the call to the server:

androidHttpTransport.call("urn:put", envelope);

and finally we wait for the response, which in this case is the identifier of the newly inserted Studente:

String result =
envelope.getResponse().toString();
return Integer.parseInt(result);

You can find the source of MainActivity, here.

Note that in the sources you can also find an object named Prova, used to test purpose: it contains arrays and all sort of types (int, Integer, long, Long, etc..)