使用安卓內置的pull解析器解析xml文檔,並自動映射成bean

安卓本身內置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;
	}
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章