Simplifying kSOAP2 clients – Part 2

In the previous post we saw how to simplify a kSOAP2 client by using a template based on the ksoap2-android library. In this time we will get in deep on how to deal with complex types as call parameters or return values, such as lists, arrays and complex objects (complex meaning non-primitive Java types). Again, we will use the kSOAP2 template to simplify the service client code.

In order to simplify the explanation, below are some examples showing how to treat each case. At the end, there is a link to the source code repository to download all of this.

Case 1: Single complex object as parameter

Suppose a service with the following interface:

@WebService(targetNamespace = "http://com.wordpress.jbaris/")
public interface CarService {
	void addCar(@WebParam(name = "car") Car car);
}

where the Car class is:

public class Car {
	private Long id;
	private String name;
	private Manufacturer manufacturer;
	private List<Owner> owners;
	// getters y setters
}
public class Manufacturer {
	private Long id;
	private String name;
	private String country;
	// getters y setters
}
public class Owner {
	private Long id;
	private String name
	// getters y setters
}

The SOAP request must to look like this:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:com="http://com.wordpress.jbaris/">
   <soapenv:Header/>
   <soapenv:Body>
      <com:addCar>
         <car>
            <id>?</id>
            <manufacturer>
               <country>?</country>
               <id>?</id>
               <name>?</name>
            </manufacturer>
            <name>?</name>
            <!--One or more repetitions:-->
            <owners>
               <id>?</id>
               <name>?</name>
            </owners>
         </car>
      </com:addCar>
   </soapenv:Body>
</soapenv:Envelope>

So, the client code looks like this:

KSoap2Template template;
template.call("addCar", "http://impl.services.com.wordpress.jbaris/addCar",
	new RequestPropertiesSetter() {
		@Override
		public void setValues(SoapObject request) {
			SoapObject owner = new SoapObject(null, "owners");
			owner.addProperty("id", 1L);
			owner.addProperty("name", "John");

			SoapObject owner2 = new SoapObject(null,"owners");
			owner2.addProperty("id", 2L);
			owner2.addProperty("name", "Mary");

			SoapObject manufacturer = new SoapObject(null, "manufacturer");
			manufacturer.addProperty("id", 1L);
			manufacturer.addProperty("name", "Chevrolet");
			manufacturer.addProperty("country", "USA");

			SoapObject car = new SoapObject(null, "car");
			car.addProperty("id", 1L);
			car.addProperty("name", "Aveo");
			car.addProperty("manufacturer", manufacturer);
			car.addSoapObject(owner);
			car.addSoapObject(owner2);

			request.addProperty("car", car);
	}});

Notice that the call method is invoked without a ResponseMapper parameter because it has a void return value. Moreover, the RequestPropertiesSetter parameter adds a property to the target request, property whose name (car) matchs with the web service operation name in the target WSDL. One more important thing: the involved SoapObjects must have a null namespace (the 1st. constructor parameter) and their names (the 2nd. constructor parameter) must match with the target WSDL’s complexTypes.

Case 2: Complex object list as parameter

Suppose a service with the following interface:

@WebService(targetNamespace = "http://com.wordpress.jbaris/")
public interface CarService {
	void addCarList(@WebParam(name = "cars") List<Car> cars);
}

The SOAP request must to look like this:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:com="http://com.wordpress.jbaris/">
   <soapenv:Header/>
   <soapenv:Body>
      <com:addCarList>
         <!--One or more repetitions:-->
         <cars>
            <id>?</id>
            <manufacturer>
               <country>?</country>
               <id>?</id>
               <name>?</name>
            </manufacturer>
            <name>?</name>
            <!--One or more repetitions:-->
            <owners>
               <id>?</id>
               <name>?</name>
            </owners>
         </cars>
      </com:addCarList>
   </soapenv:Body>
</soapenv:Envelope>

So, the client code looks like this:

KSoap2Template template;
template.call("addCarList", "http://impl.services.com.wordpress.jbaris/addCarList",
	new RequestPropertiesSetter() {
		@Override
		public void setValues(SoapObject request) {
			SoapObject owner = new SoapObject(null,"owners");
			owner.addProperty("id", 1L);
			owner.addProperty("name", "John");

			SoapObject manufacturer = new SoapObject(null, "manufacturer");
			manufacturer.addProperty("id", 1L);
			manufacturer.addProperty("name", "Chevrolet");
			manufacturer.addProperty("country", "USA");

			SoapObject car = new SoapObject(null, "cars");
			car.addProperty("id", 2L);
			car.addProperty("name", "Astra");
			car.addProperty("manufacturer", manufacturer);
			car.addSoapObject(owner);

			SoapObject owner2 = new SoapObject(null, "owners");
			owner2.addProperty("id", 2L);
			owner2.addProperty("name", "Mary");

			SoapObject car2 = new SoapObject(null, "cars");
			car2.addProperty("id", 3L);
			car2.addProperty("name", "Vectra");
			car2.addProperty("manufacturer", manufacturer);
			car2.addSoapObject(owner2);

			request.addSoapObject(car);
			request.addSoapObject(car2);
		}});

Again, the call hasn’t a ResponseMapper parameter because the void return value. The RequestPropertiesSetter adds two “children” objects, just as you add items to a list. The rest is similar to the previous case.

Case 3: Primitive array as parameter

Suppose a service with the following interface:

@WebService(targetNamespace = "http://com.wordpress.jbaris/")
public interface CarService {
	List<Car> getCarsByNames(@WebParam(name = "names") String[] ids);
}

The SOAP request must to look like this:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:com="http://com.wordpress.jbaris/">
   <soapenv:Header/>
   <soapenv:Body>
      <com:getCarsByNames>
         <!--Zero or more repetitions:-->
         <names>?</names>
      </com:getCarsByNames>
   </soapenv:Body>
</soapenv:Envelope>

So, the client code looks like this:

KSoap2Template template;
List<Car> cars = template.call("getCarsByNames", "http://impl.services.com.wordpress.jbaris/getCarsByNames",
	new RequestPropertiesSetter() {
		@Override
		public void setValues(SoapObject request) {
			request.addProperty("names", "Vectra");
			request.addProperty("names", "Astra");
		}
	}, carListResponseMapper);

In this case, the RequestPropertiesSetter is almost trivial, because the involved parameters are primitives items of an array. The special feature is the multiple invocations to the addProperty method, one per item. However, the key of this call is on the ResponseMapper (called carListResponseMapper). A mapper have to convert the web service response into a Java object, in this case a list of Cars. We’ll dig deeper into mappers in the next case.

Case 4: Primitive list as return value

Suppose a service with the following interface:

@WebService(targetNamespace = "http://com.wordpress.jbaris/")
public interface CarService {
	List<String> getCarNamesList();
}

The SOAP request must to look like this:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:com="http://com.wordpress.jbaris/">
   <soapenv:Header/>
   <soapenv:Body>
      <com:getCarNamesList/>
   </soapenv:Body>
</soapenv:Envelope>

So, the client code looks like this:

List<String> carNames = getTemplate().call("getCarNamesList", "http://impl.services.com.wordpress.jbaris/getCarNamesList",
	new AbstractResponseMapper<List<String>>() {
		@Override
		public List<String> mapResponse(Object response) {
			return getList(response);
		}
	});

This time, there is no RequestPropertiesSetter because getCarNamesList don’t take parameters. However, the call uses a ResponseMapper to convert the service response into list of String. AbstractResponseMapper is an abstract class that aims to simplifying the mapper implementations by providing the most common conversion methods, e.g. the getList method invoked above.

Case 5: Complex object array as return value

Suppose a service with the following interface:

@WebService(targetNamespace = "http://com.wordpress.jbaris/")
public interface CarService {
	Car[] getCarsArray();
}

The SOAP request must to look like this:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:com="http://com.wordpress.jbaris/">
   <soapenv:Header/>
   <soapenv:Body>
      <com:getCarsArray/>
   </soapenv:Body>
</soapenv:Envelope>

So, the client code looks like this:

KSoap2Template template;
Car[] cars = template.call("getCarsArray",
				"http://impl.services.com.wordpress.jbaris/getCarsArray",
				carArrayResponseMapper);

Again, this call don’t uses a RequestPropertiesSetter because getCarsArray don’t take parameters. All the magic is on the ResponseMapper (called carArrayResponseMapper):

ResponseMapper<Car[]> carArrayResponseMapper = new AbstractResponseMapper<Car[]>() {
	@Override
	public Car[] mapResponse(Object response) {
		return getArray((Collection<Object>) response, 
				carResponseMapper, Car.class);
	}
};
ResponseMapper<Car> carResponseMapper = new AbstractResponseMapper<Car>() {
	@Override
	public Car mapResponse(Object response) {
		Car result = new Car();
		result.setId(getProperty((SoapObject) response, 
				"id", Long.class));
		result.setManufacturer(getProperty((SoapObject) response,
			"manufacturer", manufacturerResponseMapper));
		result.setName(getProperty((SoapObject) response, "name"));
		result.setOwners(getList((SoapObject) response, 
			"owners", ownerResponseMapper));
		return result;
	}
};
ResponseMapper<Manufacturer> manufacturerResponseMapper = new AbstractResponseMapper<Manufacturer>() {
	@Override
	public Manufacturer mapResponse(Object response) {
		Manufacturer result = new Manufacturer();
		result.setCountry(getProperty((SoapObject) response, "country"));
		result.setId(getProperty((SoapObject) response, 
				"id", Long.class));
		result.setName(getProperty((SoapObject) response, "name"));
		return result;
	}
};
ResponseMapper<Owner> ownerResponseMapper = new AbstractResponseMapper<Owner>() {
	@Override
	public Owner mapResponse(Object response) {
		Owner result = new Owner();
		result.setId(getProperty((SoapObject) response, 
				"id", Long.class));
		result.setName(getProperty((SoapObject) response, "name"));
		return result;
	}
};

Notice that a ResponseMapper can use another ResponseMapper to meet its purpose (e.g. carArrayResponseMapper uses carResponseMapper). Once again, all the mappers extend to AbstractResponseMapper to inherits the common conversion methods.

For a complete set of call alternatives, there is a GitHub hosted project with a JUnit test case (called KSoap2TemplateTest). Feel free to download it and make pull requests to improve it. Again, this code may be incomplete or can be improved, so comments are welcome.

5 Responses to “Simplifying kSOAP2 clients – Part 2”


  1. 1 killerwilmer June 22, 2012 at 10:40 pm

    I have this web service

    ——————————————————————————
    @WebService(serviceName = “WSSincronizacion”)
    public class WSSincronizacion {

    /**
    * This is a sample web service operation
    */
    @WebMethod(operationName = “consultarProductos”)
    public ArrayList consultarProductos(@WebParam(name = “idCategoria”) int idCategoria) {

    ArrayList lista = null;

    try{
    Producto prod = new Producto();
    lista = new ArrayList();
    lista = prod.consultarPorCategoria(idCategoria);
    }
    catch(Exception ex){
    System.out.println(ex.getMessage());
    }

    return lista;
    }
    }
    ——————————————————————————

    as the parameter shipping and receiving the list of objects
    thanks

    sorry for my English.

  2. 5 Abhinandan January 7, 2014 at 11:00 am

    how do i pass ArrayList to .net web service


Leave a comment




Enhanced Links