xml-rpc的文档规范请参照博文《XML-RPC 规范》, 本文是其android版本的一个实现,代码摘自wordpress的官方android发布版。
主要XML的封装和解封代码实现如下, 实现了将Java的数据类型封装成xml-rpc请求格式的xml数据和解析xml格式的返回数据来转换成java的数据类型:
package org.xmlrpc.android; import java.io.BufferedReader; import java.io.DataInputStream; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.StringReader; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.TimeZone; import java.util.Map.Entry;
import org.wordpress.android.models.MediaFile; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; class XMLRPCSerializer { static final String TAG_NAME = "name"; static final String TAG_MEMBER = "member"; static final String TAG_VALUE = "value"; static final String TAG_DATA = "data"; static final String TYPE_INT = "int"; static final String TYPE_I4 = "i4"; static final String TYPE_I8 = "i8"; static final String TYPE_DOUBLE = "double"; static final String TYPE_BOOLEAN = "boolean"; static final String TYPE_STRING = "string"; static final String TYPE_DATE_TIME_ISO8601 = "dateTime.iso8601"; static final String TYPE_BASE64 = "base64"; static final String TYPE_ARRAY = "array"; static final String TYPE_STRUCT = "struct"; static SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd'T'HH:mm:ss"); /** * 序列化请求数据,封装成对应的XML格式. * * 请求示例: * * <methodCall> * <methodName>examples.getStateName</methodName> * <params> * <param> * <value><i4>41</i4></value> * </param> * </params> * </methodCall> * * 结构体: * <struct> * <member> * <name>lowerBound</name> * <value><i4>18</i4></value> * </member> * <member> * <name>upperBound</name> * <value><i4>139</i4></value> * </member> * </struct> * * 数组: * <array> * <data> * <value><i4>12</i4></value> * <value><string>Egypt</string></value> * <value><boolean>0</boolean></value> * <value><i4>-31</i4></value> * </data> * </array> * * */ static void serialize(XmlSerializer serializer, Object object, boolean convertToGMT) throws IOException { // check for scalar types: if (object instanceof Integer || object instanceof Short || object instanceof Byte) { // 基本类型: 整型 serializer.startTag(null, TYPE_I4).text(object.toString()).endTag(null, TYPE_I4); } else if (object instanceof Long) { // 基本类型: 长整型 serializer.startTag(null, TYPE_I8).text(object.toString()).endTag(null, TYPE_I8); } else if (object instanceof Double || object instanceof Float) { // 基本类型: 浮点型 serializer.startTag(null, TYPE_DOUBLE).text(object.toString()).endTag(null, TYPE_DOUBLE); } else if (object instanceof Boolean) { // 基本类型: 布尔型 Boolean bool = (Boolean) object; String boolStr = bool.booleanValue() ? "1" : "0"; serializer.startTag(null, TYPE_BOOLEAN).text(boolStr).endTag(null, TYPE_BOOLEAN); } else if (object instanceof String) { // 基本类型: 字符串型 serializer.startTag(null, TYPE_STRING).text(object.toString()).endTag(null, TYPE_STRING); } else if (object instanceof Date || object instanceof Calendar) { // 基本类型: 日期型 Date date = (Date) object; SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd'T'HH:mm:ss"); if (convertToGMT) { long pubDate = date.getTime(); TimeZone tz = TimeZone.getDefault(); long offset = tz.getOffset(pubDate); pubDate += offset * -1; date = new Date(pubDate); } String sDate = dateFormat.format(date); serializer.startTag(null, TYPE_DATE_TIME_ISO8601).text(sDate).endTag(null, TYPE_DATE_TIME_ISO8601); } else if (object instanceof byte[]) { // 基本类型: Base64编码型 String value = new String(Base64Coder.encode((byte[]) object)); serializer.startTag(null, TYPE_BASE64).text(value).endTag(null, TYPE_BASE64); } else if (object instanceof MediaFile) { // 基本类型: Base64编码型 // convert media file binary to base64 serializer.startTag(null, "base64"); MediaFile videoFile = (MediaFile) object; InputStream inStream = new DataInputStream(new FileInputStream(videoFile.getFilePath())); byte[] buffer = new byte[3600];// you must use a 24bit multiple int length = -1; String chunk = null; // int ctr = 0; // Log.i("WordPress", "converting media file to base64"); while ((length = inStream.read(buffer)) > 0) { chunk = Base64.encodeBytes(buffer, 0, length); serializer.text(chunk); // ctr+=3600; // Log.i("WordPress", "chunk " + ctr); } // Log.i("WordPress", "conversion done!"); inStream.close(); serializer.endTag(null, "base64"); } else if (object instanceof List<?>) { // 复合类型: 数组型 serializer.startTag(null, TYPE_ARRAY).startTag(null, TAG_DATA); List<Object> list = (List<Object>) object; Iterator<Object> iter = list.iterator(); while (iter.hasNext()) { Object o = iter.next(); serializer.startTag(null, TAG_VALUE); serialize(serializer, o, convertToGMT); serializer.endTag(null, TAG_VALUE); } serializer.endTag(null, TAG_DATA).endTag(null, TYPE_ARRAY); } else if (object instanceof Object[]) { // 复合类型: 数组型 serializer.startTag(null, TYPE_ARRAY).startTag(null, TAG_DATA); Object[] objects = (Object[]) object; for (int i = 0; i < objects.length; i++) { Object o = objects[i]; serializer.startTag(null, TAG_VALUE); serialize(serializer, o, convertToGMT); serializer.endTag(null, TAG_VALUE); } serializer.endTag(null, TAG_DATA).endTag(null, TYPE_ARRAY); } else if (object instanceof Map) { // 复合类型: 结构型 serializer.startTag(null, TYPE_STRUCT); Map<String, Object> map = (Map<String, Object>) object; Iterator<Entry<String, Object>> iter = map.entrySet().iterator(); while (iter.hasNext()) { Entry<String, Object> entry = iter.next(); String key = entry.getKey(); Object value = entry.getValue(); serializer.startTag(null, TAG_MEMBER); serializer.startTag(null, TAG_NAME).text(key).endTag(null, TAG_NAME); serializer.startTag(null, TAG_VALUE); serialize(serializer, value, convertToGMT); serializer.endTag(null, TAG_VALUE); serializer.endTag(null, TAG_MEMBER); } serializer.endTag(null, TYPE_STRUCT); } else { throw new IOException("Cannot serialize " + object); } } /** * 反序列化服务器的返回数据,解封成Java的数据类型. * * 回复示例: * * <methodResponse> * <params> * <param> * <value><string>South Dakota</string></value> * </param> * </params> * </methodResponse> * */ static Object deserialize(XmlPullParser parser) throws XmlPullParserException, IOException { parser.require(XmlPullParser.START_TAG, null, TAG_VALUE); parser.nextTag(); String typeNodeName = parser.getName(); Object obj; if (typeNodeName.equals(TYPE_INT) || typeNodeName.equals(TYPE_I4)) { String value = parser.nextText(); obj = Integer.parseInt(value); } else if (typeNodeName.equals(TYPE_I8)) { String value = parser.nextText(); obj = Long.parseLong(value); } else if (typeNodeName.equals(TYPE_DOUBLE)) { String value = parser.nextText(); obj = Double.parseDouble(value); } else if (typeNodeName.equals(TYPE_BOOLEAN)) { String value = parser.nextText(); obj = value.equals("1") ? Boolean.TRUE : Boolean.FALSE; } else if (typeNodeName.equals(TYPE_STRING)) { obj = parser.nextText(); } else if (typeNodeName.equals(TYPE_DATE_TIME_ISO8601)) { String value = parser.nextText(); try { obj = dateFormat.parseObject(value); } catch (ParseException e) { e.printStackTrace(); obj = value; // throw new IOException("Cannot deserialize dateTime " + value); } } else if (typeNodeName.equals(TYPE_BASE64)) { String value = parser.nextText(); BufferedReader reader = new BufferedReader(new StringReader(value)); String line; StringBuffer sb = new StringBuffer(); while ((line = reader.readLine()) != null) { sb.append(line); } obj = Base64Coder.decode(sb.toString()); } else if (typeNodeName.equals(TYPE_ARRAY)) { parser.nextTag(); // TAG_DATA (<data>) parser.require(XmlPullParser.START_TAG, null, TAG_DATA); parser.nextTag(); List<Object> list = new ArrayList<Object>(); while (parser.getName().equals(TAG_VALUE)) { list.add(deserialize(parser)); parser.nextTag(); } parser.require(XmlPullParser.END_TAG, null, TAG_DATA); parser.nextTag(); // TAG_ARRAY (</array>) parser.require(XmlPullParser.END_TAG, null, TYPE_ARRAY); obj = list.toArray(); } else if (typeNodeName.equals(TYPE_STRUCT)) { parser.nextTag(); Map<String, Object> map = new HashMap<String, Object>(); while (parser.getName().equals(TAG_MEMBER)) { String memberName = null; Object memberValue = null; while (true) { parser.nextTag(); String name = parser.getName(); if (name.equals(TAG_NAME)) { memberName = parser.nextText(); } else if (name.equals(TAG_VALUE)) { memberValue = deserialize(parser); } else { break; } } if (memberName != null && memberValue != null) { map.put(memberName, memberValue); } parser.require(XmlPullParser.END_TAG, null, TAG_MEMBER); parser.nextTag(); } parser.require(XmlPullParser.END_TAG, null, TYPE_STRUCT); obj = map; } else { throw new IOException("Cannot deserialize " + parser.getName()); } parser.nextTag(); // TAG_VALUE (</value>) parser.require(XmlPullParser.END_TAG, null, TAG_VALUE); return obj; } }
其他工具类的实现,比如错误,异常的实现类和Base64的编码和解码请参照:android_rpc