安卓本身內置XmlPullParser解析器,默認也是使用這個解析器進行xml文檔的解析。這裏自己寫了一個方法來使用XmlPullParser解析器,解析xml文檔,並自動轉換爲對應實體bean。
package com.example.org.suju.pullxmldemo;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import org.xmlpull.v1.XmlPullParser;
import android.util.Log;
import android.util.Xml;
public class PullXmlUtils<T> {
/** 當前索引的xml標籤值 */
private String tagname = null;
/** 實體類class */
private Class<T> cls;
/** 保存實體類對象list集合 */
private List<T> objs;
/** 存放對應實體類和起復合成員變量對象的method[]數組的 hash表 */
private Map<Class<?>, Method[]> methodmap;
/** 用於保存當前正在操作實體類的棧,裏面保存有實體類和其複合成員字段的實體類 */
private Stack<Object> stack;
/**
* 初始化類
* @param cls
*/
public PullXmlUtils(Class<T> cls)
{
this.cls = cls;
this.stack = new Stack<Object>();
this.methodmap = new HashMap<Class<?>, Method[]>();
this.methodmap.put(cls, cls.getDeclaredMethods());
}
/**
* 解析標籤方法,返回包裝解析內容的bean集合
* @param inputStream 輸入解析流
* @return 解析bean類集合
* @throws Exception
*/
public List<T> parse(InputStream inputStream) throws Exception
{
XmlPullParser pull = Xml.newPullParser();
//默認utf-8解析編碼
pull.setInput(inputStream, "UTF-8");
//獲取pull解析事件類型
int type = pull.getEventType();
while (type != XmlPullParser.END_DOCUMENT) {
switch (type) {
//文檔開始事件
case XmlPullParser.START_DOCUMENT:
objs = new ArrayList<T>();
break;
//開始解析標籤事件
case XmlPullParser.START_TAG:
startTag(pull);
break;
//標籤結束事件
case XmlPullParser.END_TAG:
endTag(pull.getName());
break;
}
type = pull.next();
}
return objs;
}
/**
* 標籤結束處理
* @param qName 當前標籤名稱
*/
private void endTag(String qName)
{
//當前標籤值是實體類標籤,stack出棧,並添加到實體類集合中
if (cls.getSimpleName().equals(qName)) {
objs.add((T) stack.pop());
} else if (!stack.empty()) {
try {
//當棧不爲空時,獲取棧頂元素的class名稱,判斷是否對應qName標籤值
String clsname = stack.peek().getClass().getSimpleName();
if (clsname.equals(qName)) {
/*
* 第一次彈出棧的obj爲實體類對應的set方法的形參對象,
* 出棧後,棧頂元素就爲調用改set方法的對象。
*/
Object parent = stack.pop();
invokeSetMethod(stack.peek(), qName, parent);
}
} catch (Exception e) {
Log.d("log", e.toString());
}
}
tagname = null;
}
/**
* 解析標籤內容體
* @param pull xml解析器對象
*/
private void parseTag(XmlPullParser pull)
{
//解析字符內容
if (tagname == null) return;
String name = null;
Method[] methods = stack.empty() ?
methodmap.get(cls) : methodmap.get(stack.peek().getClass());
//遍歷method數組,如果當前tagname標籤對應其中一個method,則執行該method
for (Method method: methods) {
name = setMethodName(tagname);
if (name.equals(method.getName())) {
try {
/*
* 當前棧頂元素爲調用改method方法的對象
* new String(ch, start, length) 爲標籤值
*/
invokeSetMethod(stack.peek(), tagname, pull.nextText());
break;
} catch (Exception e) {
Log.d("log", e.toString());
}
}
}
}
/**
* 標籤開始解析處理
* @param pull xml解析器對象
* @throws Exception
*/
private void startTag(XmlPullParser pull) throws Exception
{
String qName = pull.getName();
//根據棧是否爲空來獲取默認實體類或者字段所對應qName名稱的方法
Method method = stack.empty() ?
getMethod(cls, qName) : getMethod(stack.peek().getClass(), qName);
try {
if (method != null) {
//獲取方法形參類型,這裏定義set方法只有一個參數
Class<?> paramtype = method.getParameterTypes()[0];
//獲取對應參數類型的枚舉值,並設置標籤屬性值
TypeEnum te = TypeEnum.valueOfTE(paramtype.getSimpleName());
if (te == TypeEnum.EMPTY) {
setAttr(paramtype, pull);
}
} else if (cls.getSimpleName().equals(qName)) {
//當前標籤爲實體類標籤時,設置實體類對應的xml標籤屬性值
setAttr(cls, pull);
}
} catch (Exception e) {
Log.d("log", e.toString());
}
//設置當前索引到的標籤
tagname = qName;
parseTag(pull);
}
/**
*
* @param paramtype 形參類型
* @param attributes 屬性值對象
* @throws Exception
*/
private void setAttr(Class<?> paramtype, XmlPullParser pull) throws Exception
{
//實例化形參class對應的對象,併入棧
stack.push(paramtype.newInstance());
if (!methodmap.containsKey(paramtype)) {
//如果method的hash表不包含此class的方法,則保存方法數組到hash表
methodmap.put(paramtype, paramtype.getDeclaredMethods());
}
//遍歷設置屬性值
for (int i=0; i < pull.getAttributeCount(); i++) {
invokeSetMethod(stack.peek(), pull.getAttributeName(i), pull.getAttributeValue(i));
}
}
/**
*
* @param cls 實體類中方法的形參參數類型
* @param obj 實體類set方法傳入的參數對象
* @return 根據參數類型轉換參數對象值
*/
private Object changeType(Class<?> cls, Object obj)
{
//根據cls名稱獲取對應類型枚舉值,沒有返回EMPTY
TypeEnum te = TypeEnum.valueOfTE(cls.getSimpleName());
String value = obj.toString();
switch (te) {
case BOOLEAN:
return Boolean.valueOf(value);
case BYTE:
return Byte.valueOf(value);
case CHAR:
return value.charAt(0);
case DOUBLE:
return Double.valueOf(value);
case SHORT:
return Short.valueOf(value);
case FLOAT:
return Float.valueOf(value);
case INT:
return Integer.valueOf(value);
case LONG:
return Long.valueOf(value);
case STRING:
return value;
default:
//不對應以上java自帶基本類型,返回原始obj對象
return obj;
}
}
/**
* 此方法待合併
* @param name 符合javabean規範的類的方法名
* @return 返回getXXX形式無參數方法名
*/
private String setMethodName(String name)
{
StringBuilder sb = new StringBuilder(name);
//替換首字符爲大寫
char first = sb.charAt(0);
sb.setCharAt(0, Character.toUpperCase(first));
sb.insert(0, "set");
return sb.toString();
}
/**
*
* @param obj 執行方法的對象
* @param mname 方法名稱
* @param args 方法參數列表
* @throws Exception
*/
private void invokeSetMethod(
Object obj, String mname, Object...args
) throws Exception
{
//根據對象class和方法名稱獲取method,這裏指定set方法不重複,並且只有一個形參
Method method = getMethod(obj.getClass(), mname);
//得到該方法的形參class列表
Class<?>[] types = method.getParameterTypes();
method.invoke(obj, changeType(types[0], args[0]));
}
/**
*
* @param cls 獲取method的class類
* @param name 方法名稱
* @return 返回獲取的method,沒有則返回null
*/
private Method getMethod(Class<?> cls, String name)
{
name = setMethodName(name);
//在已有的method數組hash表中獲取已經保存的方法數組
Method[] methods = methodmap.get(cls);
for (Method method: methods) {
if (name.equals(method.getName())) {
return method;
}
}
return null;
}
}