bish bish - 6 months ago 74
Android Question

KSOAP2 Handling complex response (vector)

I'm struggeling on getting a complex type (list) out of my response for three days now, but always getting a ClassCastException

D/SOAPEnvelope(1552): Error: java.lang.ClassCastException: org.ksoap2.serialization.SoapObject cannot be cast to com.example.webservice.ResponsiblepartyGetResponse


I read several linked pages / tutorials so often that I almost can tell them by mind, including these four very helpful pages:



and many more, but I still don't get it :(

I hope I'll give you all the input you need to show me my mistake and give me a hint to fix it. The webservice is already successfully implemented in a rich client application and I have to build an android app now using the same webservice.

My first step is to handle a "responsibleparty_get"-Response.

My response looks (with a limit to 3, but can be more/less) like this. Note: There are two additional properties of the "responsiblePartyFacades" that are just null for these responses (see below in the JAVADOC).

responsibleparty_getResponse{
responsiblePartyFacades=anyType{
id=1;
name=hans;
discriminator=person;
admin=false;
};
responsiblePartyFacades=anyType{
id=2;
name=dieter;
discriminator=person;
admin=false;
};
responsiblePartyFacades=anyType{
id=3;
name=stefan;
discriminator=person;
admin=false;
};


}

The related XML looks like this (got out of SoapUI):

<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
<S:Body>
<ns2:responsibleparty_getResponse xmlns:ns2="http://ws.my-url.com/">
<responsiblePartyFacades>
<id>1</id>
<name>hans</name>
<discriminator>person</discriminator>
<admin>false</admin>
</responsiblePartyFacades>
<responsiblePartyFacades>
<id>2</id>
<name>dieter</name>
<discriminator>person</discriminator>
<admin>false</admin>
</responsiblePartyFacades>
<responsiblePartyFacades>
<id>3</id>
<name>stefan</name>
<discriminator>person</discriminator>
<admin>false</admin>
</responsiblePartyFacades>
</ns2:responsibleparty_getResponse>
</S:Body>
</S:Envelope>


From our JAVADOC (out of the rich client) the responsiblePartyFacades looks the same:

* &lt;complexType name="responsiblePartyFacade">
* &lt;complexContent>
* &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
* &lt;sequence>
* &lt;element name="id" type="{http://www.w3.org/2001/XMLSchema}int" minOccurs="0"/>
* &lt;element name="name" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/>
* &lt;element name="discriminator" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/>
* &lt;element name="admin" type="{http://www.w3.org/2001/XMLSchema}boolean" minOccurs="0"/>
* &lt;element name="children" type="{http://ws.my-url.com/}pairIntBoolFacade" maxOccurs="unbounded" minOccurs="0"/>
* &lt;element name="plantcomponents" type="{http://www.w3.org/2001/XMLSchema}int" maxOccurs="unbounded" minOccurs="0"/>
* &lt;/sequence>
* &lt;/restriction>
* &lt;/complexContent>
* &lt;/complexType>


So far, so nice.

I wrote these classes to handle the reponse:

ResponsiblepartyGetResponse

public class ResponsiblepartyGetResponse extends Vector<ResponsiblePartyFacade> implements KvmSerializable {

private static final long serialVersionUID = -8065084038519643055L;

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

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

@Override
public void getPropertyInfo(int index, Hashtable properties, PropertyInfo info) {
info.name = "ResponsiblePartyFacades";
info.type = new ResponsiblePartyFacade().getClass();

}

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

// add ResponsiblePartyFacade to vector
this.add((ResponsiblePartyFacade) value);

}


}

ResponsiblePartyFacade: For now i comment out the vecor-items as they are not in the response, because I throught it might work (as there are several blogs on the web saying ksoap2 vector does not work).

public final class ResponsiblePartyFacade implements KvmSerializable {
private Integer id;
private String name;
private String discriminator;
private Boolean admin;
// private List<PairIntBoolFacade> children; // not used
// private List<Integer> plantcomponents; // not used


public ResponsiblePartyFacade() {
}


public Integer getId() {
return id;
}



public void setId(Integer id) {
this.id = id;
}


public String getName() {
return name;
}



public void setName(String name) {
this.name = name;
}


public String getDiscriminator() {
return discriminator;
}


public void setDiscriminator(String discriminator) {
this.discriminator = discriminator;
}

public Boolean getAdmin() {
return admin;
}


public void setAdmin(Boolean admin) {
this.admin = admin;
}

/*
public List<PairIntBoolFacade> getChildren() {
if (children == null) {
children = new ArrayList<PairIntBoolFacade>();
}
return children;
}


public void setChildren(List<PairIntBoolFacade> children) {
this.children = children;
}



public List<Integer> getPlantcomponents() {
if (plantcomponents == null) {
plantcomponents = new ArrayList<Integer>();
}
return plantcomponents;
}


public void setPlantcomponents(List<Integer> plantcomponents) {
this.plantcomponents = plantcomponents;
}
*/



public int getPropertyCount() {
return 4;
}



public Object getProperty(int __index) {
switch(__index) {
case 0: return id;
case 1: return name;
case 2: return discriminator;
case 3: return admin;
//case 4: return children;
//case 5: return plantcomponents;
}
return null;
}

public void setProperty(int __index, Object value) {
switch(__index) {
case 0: id = Integer.parseInt(value.toString()); break;
case 1: name = value.toString(); break;
case 2: discriminator = value.toString(); break;
case 3: admin = Boolean.parseBoolean(value.toString()); break;
//case 4: children = (List) __obj; break;
//case 5: plantcomponents = (List) __obj; break;


}
}

public void getPropertyInfo(int __index, Hashtable __table, PropertyInfo __info) {
switch(__index) {
case 0:
__info.name = "id";
__info.type = Integer.class; break;
case 1:
__info.name = "name";
__info.type = String.class; break;
case 2:
__info.name = "discriminator";
__info.type = String.class; break;
case 3:
__info.name = "admin";
__info.type = Boolean.class; break;
/*
case 4:
__info.name = "children";
__info.type = PropertyInfo.VECTOR_CLASS;
case 5:
__info.name = "plantcomponents";
__info.type = PropertyInfo.VECTOR_CLASS;
*/
}
}

}


Here is my webservice call:

SoapObject request = new SoapObject(NAMESPACE, METHOD_NAME);

// For testing I only want to have 3 results
request.addProperty("limit", "3");


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

// add mapping
// ResponsiblepartyGetResponse got namespace in XML
envelope.addMapping(NAMESPACE, "ResponsiblepartyGetResponse", new ResponsiblepartyGetResponse().getClass());

// ResponsiblePartyFacades doesn't have namespace. I also tried to add namespace, but same result
envelope.addMapping("", "ResponsiblePartyFacades", new ResponsiblePartyFacade().getClass());

ResponsiblepartyGetResponse result = new ResponsiblepartyGetResponse();

try {
AuthTransportSE androidHttpTransport = new AuthTransportSE(URL, USERNAME, PASSWORD);
androidHttpTransport.debug = true;

androidHttpTransport.call(SOAP_ACTION, envelope);

// this shows me my result as a string, see above
System.out.println(envelope.bodyIn.toString());

// Trying to case the response -> Exception
result = (ResponsiblepartyGetResponse) envelope.bodyIn;

} catch (Exception e){
Log.d("SOAPEnvelope", "Error: "+e.toString());
}

return result;


As in some tutorials shown I also tried to parse the response manually. But for this the response has to be casted to "SoapObject" like this

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


When I do this I get the following exception:

05-11 07:09:56.389: D/SOAPEnvelope(2209): Error: java.lang.ClassCastException: java.util.Vector cannot be cast to org.ksoap2.serialization.SoapObject
05-11 07:09:56.402: D/SOAPEnvelope(2209): java.lang.ArrayIndexOutOfBoundsException: length=0; index=0


Can anybody give me a hint?

Answer

After despairing one more day I found a soultion that goes back to "misco" in java.lang.ClassCastException: org.ksoap2.serialization.SoapObject

I don't try to cast the Response anymore, but building the List myself by hand:

androidHttpTransport.call(SOAP_ACTION, envelope);
ArrayList<ResponsiblePartyFacades> returnlist = new ArrayList<ResponsiblePartyFacades>();

java.util.Vector<SoapObject> rs = (java.util.Vector<SoapObject>) envelope.getResponse();

if (rs != null)
{
    for (SoapObject cs : rs)
    {
      ResponsiblePartyFacades rp = new ResponsiblePartyFacades();

      rp.setId(Integer.parseInt(cs.getProperty(0).toString()));

      rp.setName(cs.getProperty(1).toString());
      rp.setDiscriminator(cs.getProperty(2).toString());
      rp.setAdmin(Boolean.parseBoolean(cs.getProperty(3).toString()));

      Log.d("WS", "ID = "+rp.getId() +" name = "+rp.getName()+" disc = "+rp.getDiscriminator()+" admin = "+rp.getAdmin().toString() );

        returnlist.add(rp);
    }
}