Sunday, November 11, 2012

Serializing and Parceling KSOAP2 SoapObject in Android

Using KSOAP2 in Android Salesforce integration, there are many occasions needing a KSOAP2’s SoapObject passing among Bundle Extra. However, SoapObject doesn’t implement Java’s Serializable, this rule out the use of out of the box features of Andrioid’s putExtra(… Serializable) function call.

Without changing the source code of SoapObject, I make a Parcelable wrapper and serialize SoapObject into byte array to facilitate the object passing through Android’s Bundle. The very first task is how to serialize and de-serialize SoapObject …

How to serialize SoapObject ?


KSOAP2’s SoapSerializationEnvelope has a write to Serializer API that can be leveraged. The code snippet serializes a SoapSerializationEnvelope that contains a SoapObject into a byte array. Although we are only interested on the SoapObject, I found it’s quite difficult to serialize and de-serialize just the SoapObject unless a lot of extra coding.

SoapSerializationEnvelope envelope=new SoapSerializationEnvelope(SoapEnvelope.VER11);

envelope.setOutputSoapObject(aSoapObject);
XmlSerializer aSerializer=Xml.newSerializer();
ByteArrayOutputStream os=new ByteArrayOutputStream();
try {
aSerializer.setOutput(os, UTF8_Encoding);
envelope.write(aSerializer);
aSerializer.flush();
} catch (Exception e) {
e.printStackTrace();
}

byte[] bytes=os.toByteArray();


How to de-serialize SoapObject ?


Now, we got a byte array, the code snippet illustrates how to reconstruct the SoapObject from the byte array.

SoapSerializationEnvelope envelope=new SoapSerializationEnvelope(SoapEnvelope.VER11);
SoapObject soap=null;
try {
ByteArrayInputStream inputStream=new ByteArrayInputStream(bytes);
XmlPullParser p=Xml.newPullParser();
p.setInput(inputStream, UTF8_Encoding);
envelope.parse(p);
soap=(SoapObject)envelope.bodyIn;
} catch (Exception e) {
e.printStackTrace();
}


How to parcel SoapObject ?


ParcelableSoapObject (source code shows below) wrap SoapObject and implements Parcelable. Besides of the bytes array, two more strings (namespace and the name of the SoapObject) are required to write to a Parcel. The easier way is to use the Parcel’s write Serializable function; the SerializedSoap is created as a connivance class (source code below).


Source of ParcelableSoapObject

package com.k1computing.salesforce.afw;

import org.ksoap2.serialization.SoapObject;
import android.os.Parcel;
import android.os.Parcelable;

public class ParcelableSoapObject implements Parcelable {
private SoapObject soapObject;


public static final Parcelable.Creator CREATOR
= new Parcelable.Creator() {
public ParcelableSoapObject createFromParcel(Parcel in) {
ParcelableSoapObject pSObject=new ParcelableSoapObject();
SerializedSoap sSoap=(SerializedSoap)in.readSerializable();
pSObject.setSoapObject(sSoap.toSoapObject());
return pSObject;
}

public ParcelableSoapObject[] newArray(int size) {
return new ParcelableSoapObject[size];
}
};



@Override
public int describeContents() {
return 0;
}

@Override
public void writeToParcel(Parcel parcel, int arg1) {
SerializedSoap aSerializedSoap=new SerializedSoap(getSoapObject());
parcel.writeSerializable(aSerializedSoap);
}

public SoapObject getSoapObject() {
return soapObject;
}

public void setSoapObject(SoapObject soapObject) {
this.soapObject = soapObject;
}

public String getType(){
Object o=(getSoapObject()!=null)?getSoapObject().getProperty("type"):null;
return (o!=null)?o.toString():null;
}

public String getName(){
Object o=(getSoapObject()!=null)?getSoapObject().getProperty("name"):null;
return (o!=null)?o.toString():null;
}

public String getId(){
Object o=(getSoapObject()!=null)?getSoapObject().getProperty("Id"):null;
return (o!=null)?o.toString():null;
}

}



Source of SerializedSoap


package com.k1computing.salesforce.afw;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.Serializable;

import org.ksoap2.SoapEnvelope;
import org.ksoap2.serialization.SoapObject;
import org.ksoap2.serialization.SoapSerializationEnvelope;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlSerializer;

import android.util.Xml;

public class SerializedSoap implements Serializable {
static final public String UTF8_Encoding="UTF-8";

public String namespace;
public String name;
public byte[] bytes;

public SerializedSoap(String namespace, String name, byte[] bytes){
this.namespace=namespace;
this.name=name;
this.bytes=bytes;
}

public SerializedSoap(SoapObject soap){
SoapSerializationEnvelope envelope=new SoapSerializationEnvelope(SoapEnvelope.VER11);
envelope.setOutputSoapObject(soap);
XmlSerializer aSerializer=Xml.newSerializer();
ByteArrayOutputStream os=new ByteArrayOutputStream();
try {
aSerializer.setOutput(os, UTF8_Encoding);
envelope.write(aSerializer);
aSerializer.flush();
} catch (Exception e) {
e.printStackTrace();
}
namespace=soap.getNamespace();
name=soap.getName();
bytes=os.toByteArray();
}

public SoapObject toSoapObject(){
SoapSerializationEnvelope envelope=new SoapSerializationEnvelope(SoapEnvelope.VER11);
SoapObject soap=null;
try {
ByteArrayInputStream inputStream=new ByteArrayInputStream(bytes);
XmlPullParser p=Xml.newPullParser();
p.setInput(inputStream, UTF8_Encoding);
envelope.parse(p);
soap=(SoapObject)envelope.bodyIn;
} catch (Exception e) {
e.printStackTrace();
}

return soap;
}
}