Android调用WebService系列之KSoap2对象解析

在在网络APP中有2个非常重要的节

  1. 客户端请求服务端接口的能力
  2. 客户端,服务端的对接

而我的Android调用WebService系列共四篇这是最后一篇,所要讲述的只仅仅是Android调用WebService这一种比较少用且不推荐用,但是在一些特定的场合下不得不用的调用方式。

Android调用WebService系列之封装能力Android调用WebService系列之请求调用是讲的请求服务端的能力主要是介绍APP如何拥有,或者说更好的更方便的拥有这种能力

Android调用WebService系列之对象构建传递,和本文章《Android调用WebService系列之KSoap2对象解析》就是讲的客户端,服务端的对接,也是我们俗称的握手,但又不完全是对接,只能说是部分帮助对接完成的工具。

(当你的服务端全部由自己开发的时候,你完全可以对接不搞那么复杂的对象,仅用String自定义GSON,JSON握手即可。)

接下来我要讲述的是KSoap2传递对象回来的时候我们该如何处理!

首先我们要了解一下服务端传递回的流被KSoap2解析并保存了什么。

    /**
     * The body object received with this envelope. Will be an KDom Node for
     * literal encoding. For SOAP Serialization, please refer to
     * SoapSerializationEnvelope.
     */
    public Object bodyIn;
    /**
     * The body object to be sent with this envelope. Must be a KDom Node
     * modelling the remote call including all parameters for literal encoding.
     * For SOAP Serialization, please refer to SoapSerializationEnvelope
     */
    public Object bodyOut;

当我们call之后,我们只需要获取bodyIn或者调用getResponse()就可以获得返回值。

我们看看getResponse方法的源代码

   
    /**
     * Response from the soap call. Pulls the object from the wrapper object and
     * returns it.
     * 
     * @since 2.0.3
     * @return response from the soap call.
     * @throws SoapFault
     */
    public Object getResponse() throws SoapFault {
        if (bodyIn instanceof SoapFault) {
            throw (SoapFault) bodyIn;
        }
        KvmSerializable ks = (KvmSerializable) bodyIn;
        return ks.getPropertyCount() == 0 ? null : ks.getProperty(0);
    }

返回的是将bodyIn转换成KvmSerializable获取对象的第一个属性值传回来。这时候我们可能会想,我们可不可以定义一个类型然后直接让bodyIn直接返回该类型呢?很抱歉KSoap2并没提供这样的方式。

那这个返回的对象是什么呢?

我们通过调试跟踪发现,这个值其实要么是SoapFault要么是SoapObject是实现了KvmSerializable接口的。

那么我们能不能直接强制转换成我们要的类呢,很遗憾的发现,我们不能直接这样强转。

我们看看SoapObject的定义是怎么样的。

/* Copyright (c) 2003,2004, Stefan Haustein, Oberhausen, Rhld., Germany
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The  above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE. 
 *
 * Contributor(s): John D. Beatty, Dave Dash, Andre Gerard, F. Hunter,
 * Renaud Tognelli
 *
 * */

package org.ksoap2.serialization;

import java.util.*;

/**
 * A simple dynamic object that can be used to build soap calls without
 * implementing KvmSerializable
 * 
 * Essentially, this is what goes inside the body of a soap envelope - it is the
 * direct subelement of the body and all further subelements
 * 
 * Instead of this this class, custom classes can be used if they implement the
 * KvmSerializable interface.
 */

public class SoapObject implements KvmSerializable {

    String namespace;
    String name;
    Vector info = new Vector();
    Vector data = new Vector();

    /**
     * Creates a new <code>SoapObject</code> instance.
     * 
     * @param namespace
     *            the namespace for the soap object
     * @param name
     *            the name of the soap object
     */

    public SoapObject(String namespace, String name) {
        this.namespace = namespace;
        this.name = name;
    }

    public boolean equals(Object o) {
        if (!(o instanceof SoapObject))
            return false;

        SoapObject so = (SoapObject) o;
        int cnt = data.size();

        if (cnt != so.data.size())
            return false;

        try {
            for (int i = 0; i < cnt; i++)
                if (!data.elementAt(i).equals(so.getProperty(((PropertyInfo) info.elementAt(i)).name)))
                    return false;
        } catch (Exception e) {
            return false;
        }
        return true;
    }

    public String getName() {
        return name;
    }

    public String getNamespace() {
        return namespace;
    }

    /**
     * Returns a specific property at a certain index.
     * 
     * @param index
     *            the index of the desired property
     * @return the desired property
     */
    public Object getProperty(int index) {
        return data.elementAt(index);
    }

    public Object getProperty(String name) {
        for (int i = 0; i < data.size(); i++) {
            if (name.equals(((PropertyInfo) info.elementAt(i)).name))
                return data.elementAt(i);
        }
        throw new RuntimeException("illegal property: " + name);
    }

    /**
     * Returns the number of properties
     * 
     * @return the number of properties
     */
    public int getPropertyCount() {
        return data.size();
    }

    /**
     * Places PropertyInfo of desired property into a designated PropertyInfo
     * object
     * 
     * @param index
     *            index of desired property
     * @param propertyInfo
     *            designated retainer of desired property
     */
    public void getPropertyInfo(int index, Hashtable properties, PropertyInfo propertyInfo) {
        PropertyInfo p = (PropertyInfo) info.elementAt(index);
        propertyInfo.name = p.name;
        propertyInfo.namespace = p.namespace;
        propertyInfo.flags = p.flags;
        propertyInfo.type = p.type;
        propertyInfo.elementType = p.elementType;
    }

    /**
     * Creates a new SoapObject based on this, allows usage of SoapObjects as
     * templates. One application is to set the expected return type of a soap
     * call if the server does not send explicit type information.
     * 
     * @return a copy of this.
     */
    public SoapObject newInstance() {
        SoapObject o = new SoapObject(namespace, name);
        for (int i = 0; i < data.size(); i++) {
            PropertyInfo propertyInfo = (PropertyInfo) info.elementAt(i);
            o.addProperty(propertyInfo, data.elementAt(i));
        }
        return o;
    }

    /**
     * Sets a specified property to a certain value.
     * 
     * @param index
     *            the index of the specified property
     * @param value
     *            the new value of the property
     */
    public void setProperty(int index, Object value) {
        data.setElementAt(value, index);
    }

    /**
     * Adds a property (parameter) to the object. This is essentially a sub
     * element.
     * 
     * @param name
     *            The name of the property
     * @param value
     *            the value of the property
     */
    public SoapObject addProperty(String name, Object value) {
        PropertyInfo propertyInfo = new PropertyInfo();
        propertyInfo.name = name;
        propertyInfo.type = value == null ? PropertyInfo.OBJECT_CLASS : value.getClass();
        return addProperty(propertyInfo, value);
    }

    /**
     * Adds a property (parameter) to the object. This is essentially a sub
     * element.
     * 
     * @param propertyInfo
     *            designated retainer of desired property
     * @param value
     *            the value of the property
     */
    public SoapObject addProperty(PropertyInfo propertyInfo, Object value) {
        info.addElement(propertyInfo);
        data.addElement(value);
        return this;
    }

    public String toString() {
        StringBuffer buf = new StringBuffer("" + name + "{");
        for (int i = 0; i < getPropertyCount(); i++) {
            buf.append("" + ((PropertyInfo) info.elementAt(i)).name + "=" + getProperty(i) + "; ");
        }
        buf.append("}");
        return buf.toString();
    }

}

看到这,我们发现他的数据用2个Vector存储info和data。

那么我们怎么取?

网上有很多文章基本上都是直接通过循环取下标值的方式然后依次进行

我们跟踪发现,KSoap2封装后传回的SoapObject是一次嵌套排序所有借点内容。意味着如果你类中有个列表,那么也是依次排列的。而且最糟糕的是你会发现所有name都是anyType。这样意味着我们也将不能直接通过循环下标赋值的方式将他序列化到类。而且就算可以我们每一个地方要写的代码量,做的重复枯燥的工作就要增加很多。

显然这样并不是我们想要的。我们需要的是一个直接传递回来SoapObject然后直接将SoapObject转换成我们所需要的类。

于是我写了下面这个类。

原理很简单,就是通过获取SoapObject字符串,将SoapObject的字符串改变成GSON字符串由Gson转换成我们要的类!

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.ksoap2.serialization.SoapObject;
import org.ksoap2.serialization.SoapPrimitive;

import com.google.gson.Gson;
import android.util.Log;

/**
 * 对象传输基础类
 * 
 * @author 刘亚林
 * @e-mail [email protected]
 * 
 */
public class SoapObjectToClass {
	private static final String CLASSTAG = SoapObjectToClass.class.getName();

	/**
	 * 首字母大写
	 * 
	 * @param str
	 * @return
	 */
	public static String upCase(String str) {
		return String.valueOf(str.charAt(0)).toUpperCase()
				.concat(str.substring(1));
	}

	/**
	 * 片段是否是列表
	 * 
	 * @param cls
	 * @return
	 */
	public static boolean isList(Class cls) {
		if (cls.isAssignableFrom(List.class)
				|| cls.isAssignableFrom(ArrayList.class)) {
			return true;
		}
		return false;
	}

	/**
	 * soapobject转类
	 * 
	 * @param clazz
	 * @param soapObject
	 * @return
	 */
	public static <T extends BaseKvmSerializable> T soapToClass(Class<T> clazz,
			SoapObject soapObject) {
		String result = soapToGson(clazz, soapObject);
		System.out.println("result:" + result);
		T t = getFromGson(result, clazz);
		return t;
	}

	private static final Gson GSON_CONVERTER = new Gson();

	/**
	 * 通过GSON
	 * 
	 * @param value
	 * @param cls
	 * @return
	 */
	public static <T> T getFromGson(String value, Class<T> cls) {

		if (value != null && !value.equals("")) {
			try {
				return GSON_CONVERTER.fromJson(value, cls);
			} catch (Exception e) {
				Log.w(CLASSTAG, "JsonSyntaxException " + e.getCause());
			}
		}
		return null;
	}

	/**
	 * 如果里面=的数量大于1则认定为复杂类型
	 * 
	 * @param soStr
	 * @return
	 */
	public static boolean isDiffType(String soStr) {
		String rlstr = soStr.replace(":", "");
		if (soStr.length() - rlstr.length() > 0) {
			return true;
		}
		return false;
	}

	/**
	 * 将soapObject类型向json类型靠
	 * 
	 * @param str
	 * @return
	 */
	public static String replaceStr(String str) {
		return str.replace("=", ":").replace("; }", "}").replace(";", ",")
				.replace("anyType{}", "\"\"").replace("anyType", "");
	}

	/**
	 * 获取已经读取过了的字符起始值
	 * 
	 * @param str
	 * @param value
	 * @return
	 */
	public static int getSubLen(String str, String value) {
		int index = str.indexOf(value);
		return index + value.length();
	}

	/**
	 * 通过复杂对比的形式获取到name
	 * 
	 * @param str
	 * @param value
	 * @return
	 */
	public static String getNameByValue(String str, String value) {
		int index = str.indexOf(value);
		String sbStr = str.substring(0, index);
		int lastdotindex = sbStr.lastIndexOf(",");
		int lastkindex = sbStr.lastIndexOf("{");
		int endindex = sbStr.lastIndexOf(":");
		int startindex = lastdotindex > lastkindex ? lastdotindex : lastkindex;
		String result = sbStr.substring(startindex + 1, endindex);
		return result.trim();
	}

	/**
	 * soap对象转jsonString
	 * 
	 * @param clazz
	 * @param soapObject
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static <T> String soapToGson(Class<T> clazz, SoapObject soapObject) {
		String result = soapObject.toString();
		result = replaceStr(result);
		int sublen = 0;
		int soapCount = soapObject.getPropertyCount();
		HashMap<String, List<String>> rlmap = new HashMap<String, List<String>>();
		for (int i = 0; i < soapCount; i++) {
			Object childObject = soapObject.getProperty(i);
			if (childObject.getClass().isAssignableFrom(SoapPrimitive.class)) {
				continue;
			} else {
				SoapObject so = (SoapObject) childObject;
				String soStr = replaceStr(so.toString());
				if (isDiffType(soStr)) {
					String name = getNameByValue(result.substring(sublen),
							soStr);
					sublen = getSubLen(result, soStr);
					try {
						Field f = clazz.getDeclaredField(name);
						if (f != null) {
							if (isList(f.getType())) {
								Type fc = f.getGenericType();
								if (fc == null) {
									continue;
								}
								if (fc instanceof ParameterizedType) {
									ParameterizedType pt = (ParameterizedType) fc;
									Class genericClazz = (Class) pt
											.getActualTypeArguments()[0];
									soStr = soapToGson(genericClazz, so);
								}
								List<String> list;
								if (rlmap.containsKey(name)) {
									list = rlmap.get(name);
								} else {
									list = new ArrayList<String>();
								}
								list.add(soStr);
								rlmap.put(name, list);
							}
						}
					} catch (NoSuchFieldException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
		}
		Iterator iter = rlmap.entrySet().iterator();
		while (iter.hasNext()) {
			Map.Entry entry = (Map.Entry) iter.next();
			String key = (String) entry.getKey();
			List<String> val = (List<String>) entry.getValue();
			String listData = "[";
			String rplstr = "";
			int count = val.size();
			for (int i = 0; i < count; i++) {
				if (i == count - 1) {
					rplstr += key + ":" + val.get(i);
				} else {
					rplstr += key + ":" + val.get(i) + ",";
				}
			}
			result = result
					.trim()
					.replace(" ", "")
					.replace(rplstr.trim().replace(" ", ""),
							key + ":" + val.toString());

		}
		return result;
	}

	// private static boolean hasMethod(String methodName, Method[] method) {
	// for (Method m : method) {
	// if (methodName.equals(m.getName())) {
	// return true;
	// }
	// }
	// return false;
	// }
}

注意:BaseKvmSerializable缺少引用,这个类就是Android调用WebService系列之对象构建传递里面所提到的对象构建传递类。

有了这个类我们只需调用soapToClass就可以获取到我们想要的类了。当然你也可以调用soapToGson然后来获取gson字符串!

到此,我的Android调用WebService系列的四篇文章就结束了。

接下来我将慢慢的整理一套比较基础又带一点特殊的东西,敬请期待吧。

时间: 2024-10-11 13:12:43

Android调用WebService系列之KSoap2对象解析的相关文章

Android调用WebService系列之对象构建传递

上一篇我们讲了如何封装Android调用WebService的能力,把上一章的类加入我们便有了与WebService通讯的能力.往往我们会遇到WebService调用是通过对象来进行实际交互调用的.于是便有了这一章构建对象传递. 首先我们了解一下. Ksoap2这个开源包里面提供了一个接口 /* Copyright (c) 2003,2004, Stefan Haustein, Oberhausen, Rhld., Germany  *  * Permission is hereby grant

Android调用WebService系列之请求调用

好久没写博客,快有3年了.当初想在CTO把自己的技术文章一直延续,可却没有坚持! 开发Android网络App,通讯方式决定了你App所需的能力! 最近正在用Web Service进行通讯,那么就让我来讲讲Web Service吧! 一.了解Web Service是什么? 请查看(http://baike.baidu.com/link?url=7IdTbG7yw6FKJ_CU0NJYny74IsDrgay861ywsm0u_aBBG5zSwkvYgxVopH2iPdyr4_witJRYf_W6W

Android调用WebService系列之封装能力

上一篇,我们简单的讲述了Android如何进行WebService调用! 而往往我们在项目中会先封装一下再使用!那么我们如何进行能力封装使用呢? 一.了解共性,机制,思考可扩展,独立,可移植性. 首先在Android中通讯,我们必不可少的那便是Handler,Thread. 由于Android的机制,我们需要考虑 UI线程不能处理耗时操作,显然通讯属于耗时操作.所以我们用到Thread 子线程不能更新UI线程,所以我们需要用Handler机制来通知UI线程做出反应 由于服务器语种我们需要考虑 目

Android调用WebService之客户端实现(二)

原创文章,转载请注明出处:http://www.blog.csdn.net/tangcheng_ok 要在Android调用WebService,必须需要一些库来支持,上面的例子中是,我们通过XFire来访问WebService,但这些库对于我们Android客户端就不适合了.这里介绍一个google code上的一个开源项目Ksoap2-android,Ksoap2-android提供了一个轻量而高效的SOAP库访问WebService. 下载ksoap2-android-assembly-2

Android 调用webService(.net平台)

什么是webservice? Web service是一个平台独立的,低耦合的,自包含的.基于可编程的web的应用程序,可使用开放的XML(标准通用标记语言下的一个子集)标准来描述.发布.发现.协调和配置这些应用程序,用于开发分布式的互操作的应用程序.Web   Service所使用的是Internet上统一.开放的标准,如HTTP.XML.SOAP(简单对象访问协议).WSDL(webservice描述语言)等,所以Web   Service可以在任何支持这些标准的环境(Windows,Lin

Android调用WebService之服务端实现(一)

webserviceandroidservicemyeclipsestring服务器 目录(?)[-] 一构建WebServices 二新建一个WebService客服端进行测试 原创文章,转载请注明出处:http://www.blog.csdn.net/tangcheng_ok 这个简单的WebService服务将用来给Android客户端调用的,我们使用xfire来实现相关功能.WebService不多做介绍,google下一大堆呢,这里只是简单的搭建一个WebService让Android

C# 调用Webservice并传递序列化对象

原文:C# 调用Webservice并传递序列化对象 C#动态调用WebService注意要点 1.动态调用的url后面注意一定要加上?WSDL   例如:string _url = "http://服务器IP:端口/CITI_TRANS_WH/wsTransData_InWH.asmx?WSDL"; ---------------------------------------------------------------------------------------------

Android调用WebService

这两天给老师做地铁app的demo,与后台的交互要用WebService,还挺麻烦的.所以想写点,希望有用. Web Services(Web服务)是一个用于支持网络间不同机器互操作的软件系统,它是一种自包含.自描述和模块化的应用程序,它可以在网络中被描述.发布和调用,可以将它看作是基于网络的.分布式的模块化组件.它建立在HTTP,SOAP,WSDL这些通信协议之上,可以轻松的跨平台. 我们用的WebService就是服务器公布的一个接口,连上之后可以交互.WSDL是一份XML文档,它描述了We

【转】Android调用WebService

作者:欧阳旻 原文链接:http://express.ruanko.com/ruanko-express_34/technologyexchange5.html WebService是一种基于SOAP协议的远程调用标准,通过webservice可以将不同操作系统平台.不同语言.不同技术整合到一块.在Android SDK中并没有提供调用WebService的库,因此,需要使用第三方的SDK来调用WebService.PC版本的WEbservice客户端库非常丰富,例如Axis2,CXF等,但这些