使用sax解析xml文件,並自動根據實體類class得到映射後的實體類list集合

java中的javax.xml.parsers.SAXParser類用於解析xml文件,他是基於事件流形式解析的,其他解析xml的類和jar包還有很多,比如DOM是基於XML文檔樹結構的解析(代表有dom4j。sax的解析特點,決定其不是很佔用太大內存,當然也有弊端,這裏只是學習一下sax如何解析xml。

sax解析的一般步驟:

//從流中解析xml文件
	public List<Book> parse(InputStream is)
	{
		//得到一個sax解析器工廠
		SAXParserFactory saxfactory = SAXParserFactory.newInstance();
		try {
			//得到sax解析器,並在parse解析方法中傳入handler對象輔助解析
			SAXParser parser = saxfactory.newSAXParser();
			//實例化一個解析器輔助handler,解析事件處理方法都包含在其中
			Handler handler = new Handler(Book.class);
			parser.parse(is, handler);
			is.close();
			//返回handler解析後的list集合
			return handler.getobjs();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

自定應一個Handler類繼承DefaultHandler類。並且使用反射,來自動映射到對應的實體類,這樣,以後解析一個xml,只需要傳入指定實體類的class類型,就能返回解析後的實體類的list集合。

這裏只是實現了其中的一小部分功能。


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 javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;


public class XmlUtils {

	private String xmluri;
	
	public XmlUtils(){}
	
	public XmlUtils(String xmluri)
	{
		this.xmluri = xmluri;
	}
	
	//測試方法,解析一個url
	public void parse()
	{
		//得到一個sax解析器工廠
		SAXParserFactory saxfactory = SAXParserFactory.newInstance();
		try {
			//得到sax解析器,並在parse解析方法中傳入handler對象輔助解析
			SAXParser parser = saxfactory.newSAXParser();
			parser.parse(xmluri, new Handler(Book.class));
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	//從流中解析xml文件
	public List<Book> parse(InputStream is)
	{
		//得到一個sax解析器工廠
		SAXParserFactory saxfactory = SAXParserFactory.newInstance();
		try {
			//得到sax解析器,並在parse解析方法中傳入handler對象輔助解析
			SAXParser parser = saxfactory.newSAXParser();
			//實例化一個解析器輔助handler,解析事件處理方法都包含在其中
			Handler handler = new Handler(Book.class);
			parser.parse(is, handler);
			is.close();
			//返回handler解析後的list集合
			return handler.getobjs();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}
	
	/**
	 * 
	 * @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;
		}
	}
	
	/**
	 * 
	 * @author Administrator
	 *
	 * @param <T>
	 */
	public class Handler<T> extends DefaultHandler
	{
		/** 當前索引的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;
		
		/**
		 * 
		 * @return   返回解析後的實體類集合
		 */
		public List<T> getobjs()
		{
			return objs;
		}
		
		/**
		 * 初始化類
		 * @param cls
		 */
		public Handler(Class<T> cls)
		{
			this.cls = cls;
			this.stack = new Stack<Object>();
			this.methodmap = new HashMap<Class<?>, Method[]>();
			this.methodmap.put(cls, cls.getDeclaredMethods());
		}
		
		@Override
		public void startDocument() throws SAXException {
			// 開始解析 new一個新集合
			objs = new ArrayList<T>();
		}
		
		@Override
		public void startElement(String uri, String localName, String qName,
				Attributes attributes) throws SAXException {
			//根據棧是否爲空來獲取默認實體類或者字段所對應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, attributes);
					}
				} else if (cls.getSimpleName().equals(qName)) {
					//當前標籤爲實體類標籤時,設置實體類對應的xml標籤屬性值
					setAttr(cls, attributes);
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
			//設置當前索引到的標籤
			tagname = qName;
		}
		
		/**
		 * 
		 * @param paramtype     形參類型
		 * @param attributes    屬性值對象
		 * @throws Exception
		 */
		private void setAttr(Class<?> paramtype, Attributes attributes) 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 < attributes.getLength(); i++) {
				invokeSetMethod(stack.peek(), 
                                attributes.getLocalName(i), attributes.getValue(i));
			}
		}
		
		@Override
		public void endDocument() throws SAXException {
			// 解析結束
			super.endDocument();
		}
		
		@Override
		public void endElement(String uri, String localName, String qName)
				throws SAXException {
			//當前標籤值是實體類標籤,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) {
					e.printStackTrace();
				}
			}
			tagname = null;
		}
		
		@Override
		public void characters(char[] ch, int start, int length)
				throws SAXException {
			//解析字符內容
			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, new String(ch, start, length));
						break;
					} catch (Exception e) {
						e.printStackTrace();
					}
				}
			}
		}
		
		/*
		 * 此方法待合併
		 * @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;
		}
	}
}


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