Search code examples
androidvectorresponseksoap2

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?


Solution

  • 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);
        }
    }