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 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,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; } } }