分类:
2009-03-16 11:59:58
kSOAP2处理webservice简单的string类型返回值是很容易的。那么如何处理像亚马逊网上书店这种webservice返回的复杂描述呢?
kSOAP2自带了一个例子来说明,下面我们就讲解一下。
关于亚马逊的查询书目的webservice,你可以通过
来获知定义。
我们要关注的是它的关键词查询请求的方法,它的定义是:
<operation name="KeywordSearchRequest">
<soap:operation soapAction="" />
<input>
<soap:body use="encoded"
encodingStyle= namespace="" />
input>
<output>
<soap:body use="encoded"
encodingStyle= namespace="" />
output>
operation>
我们提交对包含指定关键词的书目查询,如果查询成功,将会返回一系列书名节点,每一本书都提供了作者、出版社、出版日期、价格等等信息。这些书名节点都在一个“Details”节点下。查询结果的总数放在TotalResults节点。每页10个结果,可以通过查看TotalPages节点来确定需要多少页。
那么,kSOAP2可以很简单地通过SoapObject的getProperty方法来得到书详细信息的节点,存储入一个Vector对象中,如下所示:
HttpTransport ht = new HttpTransport("/onca/soap3");
ht.call(null, envelope);
SoapObject result = (SoapObject) envelope.getResult();
Vector resultVector = (Vector) result.getProperty("Details");
Vector对象中实际上还是存储了一组SoapObject对象,这里的每一个SoapObject对象对应于一本书的DOM对象。
那么如何得到每一本书的书名、价格呢?
for(int i = 0; i < resultVector.size(); i++){
SoapObject detail = (SoapObject) resultVector.elementAt(i);
System.out.println("书名>>"+(String) detail.getProperty("ProductName"));
System.out.println("日期>>"+(String) detail.getProperty("ReleaseDate"));
System.out.println("价格>>"+(String) detail.getProperty("ListPrice"));
}
这样就可以了。
需要注意的是,要测试这个工程,必须到亚马逊的 注册获取Access Key ID,也就是webservice方法中的“devtag”参数所需要的Developer-Tag。
下面我们讲述如何在MIDP设备和webservice之间传递自定义类,比如这个类中不但有String类型成员变量,还有Vector之类的复杂类型。
大致思路就是,在服务器端将类实例按照一定规格(一个一个的成员变量写)序列化为byte[],将这个byte[]数组返回给kSOAP2。kSOAP2收到之后,再反序列化,将byte[]一段一段地读入类实例。
我们先来定义要传递的wsTeam类:
类定义 |
public class wsTeam{ private String wsReturnCode; private String wsPersonCount; public StringVector wsvPersonName; public byte[] serialize(); public static wsTeam deserialize(byte[] data) ; } |
其中,StringVector类是另外一个自定义类,就是简单地把String[]封装了一下,便于操作。StringVector类定义在示范代码中可以找到。
服务器端主要是序列化,所以我们来讲讲wsTeam的serialize()函数。
wsTeam的序列化函数 |
public byte[] serialize() { ByteArrayOutputStream baos = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream(baos);
try { dos.writeUTF(wsReturnCode); dos.writeUTF(wsPersonCount); wsvPersonName.writeObject(dos); baos.close(); dos.close(); } catch(Exception exc) { exc.printStackTrace(); }
return baos.toByteArray(); } |
这样,类实例就可以把自己序列化为byte[]数组。
那么,webservice可以这么提供:
服务器端 |
public class SimpleKSoapWS {
public SimpleKSoapWS () { }
public byte[] foo2(String username, String password) { wsTeam obj= new wsTeam (); return obj.serialize(); } } |
到了MIDP设备上,要能够从byte[]恢复出wsTeam类实例才行。
StringVector的序列化方法writeObject也很简单,先写入字符串数组的大小,然后再将每一个元素写入,如下所示:
StringVector的序列化 |
public class StringVector {… public synchronized void writeObject(java.io.DataOutputStream s) throws java.io.IOException { // Write out array length s.writeInt(count); // Write out all elements in the proper order. for (int i=0; i { s.writeUTF(data[i]); } } … } |
和前面的MIDlet代码差不多,只不过要kSOAP2的MarshalBase64出场了。
在kSOAP中,我们用Base64把二进制流编码为ASCII字符串,这样就可以通过XML/SOAP传输二进制数据了。
org.ksoap2.serialization.MarshalBase64的目的就是,把SOAP XML中的xsd:based64Binary元素序列化为Java字节数组(byete array)类型。类似的,kSOAP2还提供了MarshalDate、MarshalHashtable类来把相应的元素序列化为Java的Date、Hashtable类型。
使用MarshalBase64 |
import org.ksoap2.serialization.MarshalBase64;
SoapObject request = new SoapObject(serviceNamespace, methodName );
SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11); envelope.bodyOut = request; new MarshalBase64().register(envelope);
HttpTransport tx = new HttpTransport(serviceNamespace); tx.debug = true;
tx.call(null, envelope); Object Response = envelope.getResult(); |
将接收到的SoapObject强制转换为byte[]。
转换 |
byte[] by = (byte[])Response; System.out.println("succ convert!"); |
然后,再调用
反序列化 |
wsTeam wc = wsTeam.deserialize(by); |
这样,在无线设备上就得到了wsTeam类实例了。
wsTeam的deserialize函数是这么定义的:
wsTeam的反序列化函数 |
public class StringVector {… public static wsTeam deserialize(byte[] data) { ByteArrayInputStream bais = new ByteArrayInputStream(data); DataInputStream dis = new DataInputStream(bais); wsTeam wc = new wsTeam();
try { wc.wsReturnCode = dis.readUTF(); wc.wsPersonCount = dis.readUTF();
wc. wsvPersonName.readObject(dis);
bais.close(); dis.close(); } catch(Exception exc)
exc.printStackTrace(); }
return wc; } …} |
StringVector的反序列化方法readObject也很简单,先读入字符串数组的大小,就自行新建一个同样大小的字符串数组,然后再将每一个元素写入这个数组,如下所示:
StringVector的反序列化 |
public class StringVector {… public synchronized void readObject(java.io.DataInputStream s) throws java.io.IOException, ClassNotFoundException { // Read in array length and allocate array int arrayLength = s.readInt(); data = new String[arrayLength]; // 同步data的大小 count = arrayLength; // Read in all elements in the proper order. for (int i=0; i { data[i] = s.readUTF(); } }… } |
通过上面的反序列化,我们就可以通过
for (int i=0; i
System.out.println("第" + i +"个人:" +
wc.wsvPersonName.getStringAt(i));
}
来打印MIDlet上收到的类对象中的StringVector成员变量了。
利用kSOAP2提供的框架,你可以在无线设备和Internet webservice之间,既可以传递简单的数值,也可以传递各种各样的类对象。