XML解析框架比较

   转自:http://qingkangxu.iteye.com/blog/1838405

一个软件项目中,中间件配置、应用参数配置等常常都用XML文件的格式保存,XML的解析有很多的现有框架,本文主要是对包含DOM、SAX、Dom4j、JDOM、StAX等解析XML文件的代码示例,并做简单的分析及性能对比。

1,前言

    假设有如下的XML文件,其根节点为节点可以包含多个,一个为一个需要分布式部署的应用组,group下有多个标识部署应用的具体机器,其他的type属性标识机器是否为虚拟机。

    

Xml代码   收藏代码
  1. xml version="1.0" encoding="ISO-8859-1"?>  
  2. <root>  
  3.     <description>All application groups.description>  
  4.     <groups>  
  5.         <group name="bossAppGroup">  
  6.             <machine name="bossNode1" type="virtual">  
  7.                 <ip>192.168.0.11ip>  
  8.                 <hostname>bossApp1hostname>  
  9.             machine>  
  10.             <machine name="bossNode2" type="concrete">  
  11.                 <ip>192.168.0.12ip>  
  12.                 <hostname>bossApp2hostname>  
  13.             machine>  
  14.         group>  
  15.         <group name="wlanAppGroup">  
  16.             <machine name="wlanNode1" type="concrete">  
  17.                 <ip>192.168.0.21ip>  
  18.                 <hostname>wlan1hostname>  
  19.             machine>  
  20.             <machine name="wlanNode2" type="concrete">  
  21.                 <ip>192.168.0.22ip>  
  22.                 <hostname>wlan2hostname>  
  23.             machine>  
  24.         group>  
  25.     groups>  
  26. root>  

    对于以上文件,以下列举出使用不同的XML解析技术的示例代码。

 2,解析技术示例代码

    以下列举了SAX、DOM等解析示例,代码完成的功能都是一样的。需要解析前言中提到的XML文件,获取到机器列表,以组名{机器名[属性=只,]}的格式输出。如:

Java代码   收藏代码
  1. bossAppGroup{bossNode1[type=virtual,IP=192.168.0.11,hostname=bossApp1],bossNode2[type=concrete,IP=192.168.0.12,hostname=bossApp2]}  
  2. wlanAppGroup{wlanNode1[type=concrete,IP=192.168.0.21,hostname=wlan1],wlanNode2[type=concrete,IP=192.168.0.22,hostname=wlan2]}  

    2.1 公有常量类

Java代码   收藏代码
  1. package parser;  
  2. public class Constant {  
  3.   
  4.     public final static String ROOT = "root";  
  5.     public final static String GROUPS = "groups";  
  6.     public final static String GROUP = "group";  
  7.     public final static String MACHINE = "machine";  
  8.     public final static String IP = "ip";  
  9.     public final static String HOSTNAME = "hostname";  
  10.     public final static String NAME = "name";  
  11.     public final static String TYPE = "type";  
  12.       
  13.     public final static String TYPE_CONCRETE = "concrete";  
  14.     public final static String TYPE_VIRTUAL = "virtual";  
  15. }  

    2.2 Parser接口及对应的数据model

Java代码   收藏代码
  1. package parser;  
  2. import java.util.List;  
  3.   
  4. import module.Group;  
  5.   
  6.   
  7. public interface MachineParser {  
  8.   
  9.     /** 
  10.      * Return the machine list which in the given xml file. 
  11.      *  
  12.      * @param xmlFilePath 
  13.      *            The xml file path which contains all machine info. 
  14.      * @return formatted String of machines. 
  15.      */  
  16.     public List getGroupList(String xmlFilePath) throws Exception;  
  17. }  

     Group类包含了主机List属性,为了输出,重写了toString方法。

Java代码   收藏代码
  1. package module;  
  2.   
  3. import java.util.ArrayList;  
  4. import java.util.List;  
  5.   
  6. public class Group {  
  7.   
  8.     private String name;  
  9.     private List machineList;  
  10.       
  11.     public Group(String _name){  
  12.         this.name = _name;  
  13.         machineList = new ArrayList();  
  14.     }  
  15.       
  16.     public void addMachine(Machine machine){  
  17.         machineList.add(machine);  
  18.     }  
  19.       
  20.     public void removeMachine(Machine machine){  
  21.         machineList.remove(machine);  
  22.     }  
  23.       
  24.     public String getName() {  
  25.         return name;  
  26.     }  
  27.     public void setName(String name) {  
  28.         this.name = name;  
  29.     }  
  30.     public List getMachineList() {  
  31.         return machineList;  
  32.     }  
  33.     public void setMachineList(List machineList) {  
  34.         this.machineList = machineList;  
  35.     }  
  36.       
  37.     @Override  
  38.     public String toString() {  
  39.         StringBuffer sb = new StringBuffer();  
  40.         sb.append(name).append("{");  
  41.   
  42.         for (int i = 0; i < machineList.size(); i++) {  
  43.             sb.append(machineList.get(i));  
  44.             if (i != (machineList.size() - 1)) {  
  45.                 sb.append(",");  
  46.             }  
  47.         }  
  48.         sb.append("}");  
  49.         return sb.toString();  
  50.     }  
  51. }  

     Machine 类包含了主机IP和主机名属性,为了输出,也重写了toString方法。

Java代码   收藏代码
  1. package module;  
  2.   
  3. public class Machine {  
  4.   
  5.     private String name;  
  6.     private String type;  
  7.     private String ip;  
  8.     private String hostname;  
  9.       
  10.     public Machine(String _name) {  
  11.         this.name = _name;  
  12.     }  
  13.       
  14.     public String getName() {  
  15.         return name;  
  16.     }  
  17.   
  18.     public void setName(String name) {  
  19.         this.name = name;  
  20.     }  
  21.   
  22.     public String getType() {  
  23.         return type;  
  24.     }  
  25.   
  26.     public void setType(String type) {  
  27.         this.type = type;  
  28.     }  
  29.   
  30.     public String getIp() {  
  31.         return ip;  
  32.     }  
  33.     public void setIp(String ip) {  
  34.         this.ip = ip;  
  35.     }  
  36.     public String getHostname() {  
  37.         return hostname;  
  38.     }  
  39.     public void setHostname(String hostname) {  
  40.         this.hostname = hostname;  
  41.     }  
  42.       
  43.     @Override  
  44.     public String toString() {  
  45.         StringBuffer sb = new StringBuffer();  
  46.         sb.append(name).append("[type=" + type).append(",IP=" + ip)  
  47.                 .append(",hostname=" + hostname).append("]");  
  48.         return sb.toString();  
  49.     }  
  50.       
  51. }  

 

2.3 DOM解析实现

    DOM解析会比较耗内存,因为DOM需要一次性加载整个文件内容到内存中,解析大文件时不建议使用DOM。DOM解析也不需要导入第三方jar包,只需要JDK便可,比较轻量级。

Java代码   收藏代码
  1. package parser.dom;  
  2.   
  3. import java.io.IOException;  
  4. import java.util.ArrayList;  
  5. import java.util.List;  
  6.   
  7. import javax.xml.parsers.DocumentBuilder;  
  8. import javax.xml.parsers.DocumentBuilderFactory;  
  9.   
  10. import module.Group;  
  11. import module.Machine;  
  12.   
  13. import org.w3c.dom.Document;  
  14. import org.w3c.dom.NamedNodeMap;  
  15. import org.w3c.dom.Node;  
  16. import org.w3c.dom.NodeList;  
  17. import org.xml.sax.SAXException;  
  18.   
  19. import parser.Constant;  
  20. import parser.MachineParser;  
  21.   
  22. /** 
  23.  * JDK Dom Parser. All XML element or attribute be processed as one 
  24.  * "org.w3c.dom.Node" instance. 
  25.  *  
  26.  * @author xuqingkang 
  27.  *  
  28.  */  
  29. public class JDKDomParser implements MachineParser {  
  30.   
  31.     DocumentBuilder builder = null;  
  32.   
  33.     /** 
  34.      * Constructor 
  35.      * @throws Exception  
  36.      */  
  37.     public JDKDomParser() throws Exception {  
  38.         builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();  
  39.     }  
  40.   
  41.     /** 
  42.      * Implement of MachineParser#getMachineList 
  43.      */  
  44.     public List getGroupList(String xmlFilePath) throws SAXException,  
  45.             IOException {  
  46.         Document doc = builder.parse(xmlFilePath);  
  47.   
  48.         // Find All Groups  
  49.         NodeList groupList = doc.getElementsByTagName(Constant.GROUP);  
  50.         int groupCount = groupList.getLength();  
  51.   
  52.         if (groupCount <= 0) {  
  53.             return null;  
  54.         }  
  55.   
  56.         List machineGroups = new ArrayList();  
  57.         for (int i = 0; i < groupCount; i++) {  
  58.             // XML attribute value can be parse with Node#getTextContent() or  
  59.             // Node#getNodeValue() method  
  60.             NamedNodeMap groupAttrMap = groupList.item(i).getAttributes();  
  61.             // String groupName =  
  62.             // groupAttrMap.getNamedItem(Constant.NAME).getTextContent();  
  63.             String groupName = groupAttrMap.getNamedItem(Constant.NAME)  
  64.                     .getNodeValue();  
  65.   
  66.             Group group = new Group(groupName);  
  67.             parseMachinesOfGroup(groupList.item(i), group);  
  68.             machineGroups.add(group);  
  69.         }  
  70.   
  71.         return machineGroups;  
  72.     }  
  73.   
  74.     private void parseMachinesOfGroup(Node groupNode, Group group) {  
  75.         NodeList machineList = groupNode.getChildNodes();  
  76.         if (machineList == null || machineList.getLength() <= 0) {  
  77.             return;  
  78.         }  
  79.   
  80.         // Iterate  nodes of one  node.  
  81.         for (int i = 0; i < machineList.getLength(); i++) {  
  82.             Node machineNode = machineList.item(i);  
  83.             if (machineNode.getNodeName() == null  
  84.                     || !machineNode.getNodeName().equals(Constant.MACHINE)) {  
  85.                 continue;  
  86.             }  
  87.             NodeList machineChildren = machineNode.getChildNodes();  
  88.   
  89.             // When XML Attribute value, either Node#getTextContent() or  
  90.             // Node#getNodeValue() be ok.  
  91.             String mName = machineNode.getAttributes()  
  92.                     .getNamedItem(Constant.NAME).getTextContent();  
  93.             String mType = machineNode.getAttributes()  
  94.                     .getNamedItem(Constant.TYPE).getNodeValue();  
  95.             Machine machine = new Machine(mName);  
  96.             machine.setType(mType);  
  97.             for (int j = 0; j < machineChildren.getLength(); j++) {  
  98.                 Node machineChild = machineChildren.item(j);  
  99.                 if (machineChild.getNodeName().equals(Constant.IP)) {  
  100.                     machine.setIp(machineChild.getTextContent());  
  101.                 } else if (machineChild.getNodeName().equals(Constant.HOSTNAME)) {  
  102.                     machine.setHostname(machineChild.getTextContent());  
  103.                 }  
  104.             }  
  105.   
  106.             group.addMachine(machine);  
  107.         }  
  108.     }  
  109.   
  110. }  

 2.4 SAX解析实现

    SAX是基于事件的,其不会加载整个文件到内存中,属于按需取内容。一般使用SAX解析时都会借助于栈来完成元素和数据模型的衔接。

Java代码   收藏代码
  1. package parser.sax;  
  2. import java.util.List;  
  3.   
  4. import org.xml.sax.SAXException;  
  5. import org.xml.sax.XMLReader;  
  6. import org.xml.sax.helpers.XMLReaderFactory;  
  7.   
  8. import parser.MachineParser;  
  9.   
  10. import module.Group;  
  11.   
  12. /** 
  13.  * SAX的解析是以事件为驱动的,不会像DOM那样把文档全部加在到内存中。 
  14.  * SAX的解析一般借助栈来简化整个解析过程,一般一个XML节点开始入站,结束时出栈保存值。 
  15.  * @author xuqingkang 
  16.  * 
  17.  */  
  18. public class JDKSaxParser implements MachineParser {  
  19.   
  20.     XMLReader reader = null;  
  21.       
  22.     /** 
  23.      * @throws SAXException 
  24.      */  
  25.     public JDKSaxParser() throws SAXException{  
  26.         reader = XMLReaderFactory.createXMLReader();  
  27.     }  
  28.       
  29.     public List getGroupList(String xmlFilePath) throws Exception {  
  30.         ResourceHandler handler = new ResourceHandler();  
  31.         reader.setContentHandler(handler);  
  32.         reader.parse(xmlFilePath);  
  33.           
  34.         return handler.getGroupList();  
  35.     }  
  36.   
  37. }  

     对XML的处理过程主要在ResourceHandler类中

Java代码   收藏代码
  1. package parser.sax;  
  2.   
  3. import java.util.ArrayList;  
  4. import java.util.List;  
  5. import java.util.Stack;  
  6.   
  7. import module.Group;  
  8. import module.Machine;  
  9.   
  10. import org.xml.sax.Attributes;  
  11. import org.xml.sax.SAXException;  
  12. import org.xml.sax.helpers.DefaultHandler;  
  13.   
  14. import parser.Constant;  
  15.   
  16. public class ResourceHandler extends DefaultHandler {  
  17.   
  18.     List groupList = null;  
  19.   
  20.     /** 
  21.      * 节点开始时压栈,结束时出栈 
  22.      */  
  23.     Stack groupStack = new Stack();  
  24.       
  25.     /** 
  26.      * 节点开始时压栈,结束时出栈 
  27.      */  
  28.     Stack machineStack = new Stack();  
  29.       
  30.     /** 
  31.      * 对这样的节点,因为是节点+文本类型,因此需要用这个标示节点的内容 
  32.      */  
  33.     String currentNodeText;  
  34.   
  35.     /** 
  36.      * XML节点开始时的处理方法: 
  37.      * 主要是遇到节点的开始标签时需要把Group和Machine入栈 
  38.      */  
  39.     public void startElement(String uri, String localName, String qName,  
  40.             Attributes attributes) throws SAXException {  
  41.         if (Constant.GROUPS.equals(qName)) {  
  42.             groupList = new ArrayList();  
  43.         } else if (Constant.GROUP.equals(qName)) {  
  44.             String groupName = attributes.getValue(Constant.NAME);  
  45.             Group group = new Group(groupName);  
  46.             groupStack.push(group);  
  47.         }else if(Constant.MACHINE.equals(qName)){  
  48.             String machineName = attributes.getValue(Constant.NAME);  
  49.             String machineType = attributes.getValue(Constant.TYPE);  
  50.               
  51.             Machine machine = new Machine(machineName);  
  52.             machine.setType(machineType);  
  53.             machineStack.push(machine);  
  54.         }  
  55.     }  
  56.   
  57.     /** 
  58.      * XML节点直接加文本时的处理: 
  59.      * 主要是处理节点的值 
  60.      */  
  61.     public void characters(char[] ch, int start, int length)  
  62.             throws SAXException {  
  63.         currentNodeText = String.valueOf(ch, start, length);  
  64.     }  
  65.       
  66.     /** 
  67.      * XML节点结束时的处理方法: 
  68.      * 1,主要是遇到节点的结束标签时需要把Group和Machine出栈保存 
  69.      * 2,遇到节点的结束标签时需要保存当前文本为相应的属性值并清空当前文本变量 
  70.      */  
  71.     public void endElement(String uri, String localName, String qName)  
  72.             throws SAXException {  
  73.         if (Constant.GROUP.equals(qName)) {  
  74.             groupList.add(groupStack.pop());  
  75.         }else if(Constant.MACHINE.equals(qName)){  
  76.             groupStack.peek().addMachine(machineStack.pop());  
  77.         }else if(Constant.IP.equals(qName)){  
  78.             // 节点结束时“currentNodeText”的值即为IP  
  79.             machineStack.peek().setIp(currentNodeText);  
  80.             currentNodeText = null;  
  81.         }else if(Constant.HOSTNAME.equals(qName)){  
  82.             // 节点结束时“currentNodeText”的值即为HOSTNAME  
  83.             machineStack.peek().setHostname(currentNodeText);  
  84.             currentNodeText = null;  
  85.         }  
  86.     }  
  87.       
  88.     public List getGroupList() {  
  89.         return groupList;  
  90.     }  
  91. }  

 2.5 JDOM解析实现

    JDOM借助于XPath完成解析,需要jdom的jar包实现及jaxen jar包(主要用于处理XPath)实现。

Java代码   收藏代码
  1. package parser.jdom;  
  2.   
  3. import java.io.FileInputStream;  
  4. import java.util.ArrayList;  
  5. import java.util.List;  
  6.   
  7. import module.Group;  
  8. import module.Machine;  
  9.   
  10. import org.jdom.Document;  
  11. import org.jdom.Element;  
  12. import org.jdom.JDOMException;  
  13. import org.jdom.input.SAXBuilder;  
  14. import org.jdom.xpath.XPath;  
  15.   
  16. import parser.Constant;  
  17. import parser.MachineParser;  
  18.   
  19. /** 
  20.  * JDomParser结合XPath对XML节点进行解析 
  21.  * @author xuqingkang 
  22.  * 
  23.  */  
  24. public class JDomParser implements MachineParser {  
  25.   
  26.     SAXBuilder builder;  
  27.   
  28.     public JDomParser() {  
  29.         builder = new SAXBuilder();  
  30.     }  
  31.   
  32.     public List getGroupList(String xmlFilePath) throws Exception {  
  33.         FileInputStream stream = null;  
  34.         try {  
  35.             stream = new FileInputStream(xmlFilePath);  
  36.   
  37.             Document document = builder.build(stream);  
  38.             Element root = document.getRootElement();  
  39.   
  40.             List groupElements = XPath.selectNodes(root, "/"  
  41.                     + Constant.ROOT + "/" + Constant.GROUPS + "/"  
  42.                     + Constant.GROUP + "[name=wlanAppGroup]");  
  43.             return parse(root);  
  44.         } finally {  
  45.             try {  
  46.                 if (stream != null) {  
  47.                     stream.close();  
  48.                 }  
  49.             } catch (Exception e) {  
  50.             }  
  51.         }  
  52.     }  
  53.   
  54.     private List parse(Element root) throws JDOMException {  
  55.         List groupList = new ArrayList();  
  56.         // 节点是从根节点开始select,因此xpath需要使用绝对路径(/打头)  
  57.         String xPath = "/" + Constant.ROOT + "/" + Constant.GROUPS + "/"  
  58.                 + Constant.GROUP;  
  59.         // 节点有多个,因此需要使用XPath.selectNodes  
  60.         List groupElements = XPath.selectNodes(root, xPath);  
  61.   
  62.         for (int i = 0; i < groupElements.size(); i++) {  
  63.             Element groupElement = groupElements.get(i);  
  64.   
  65.             String groupName = groupElement.getAttributeValue(Constant.NAME);  
  66.             Group group = new Group(groupName);  
  67.             parseGroup(groupElement, group);  
  68.             groupList.add(group);  
  69.         }  
  70.   
  71.         return groupList;  
  72.     }  
  73.   
  74.     private void parseGroup(Element groupElement, Group group)  
  75.             throws JDOMException {  
  76.         // 一个节点下有多个节点,因此需要使用XPath.selectNodes  
  77.         List machineElements = XPath.selectNodes(groupElement,  
  78.                 Constant.MACHINE);  
  79.         for (int i = 0; i < machineElements.size(); i++) {  
  80.             Element machineElement = machineElements.get(i);  
  81.             String machineName = machineElement  
  82.                     .getAttributeValue(Constant.NAME);  
  83.             String machineType = machineElement  
  84.                     .getAttributeValue(Constant.TYPE);  
  85.             String ip = ((Element) XPath.selectSingleNode(machineElement,  
  86.                     Constant.IP)).getText();  
  87.             String hostname = ((Element) XPath.selectSingleNode(machineElement,  
  88.                     Constant.HOSTNAME)).getText();  
  89.   
  90.             Machine machine = new Machine(machineName);  
  91.             machine.setType(machineType);  
  92.             machine.setIp(ip);  
  93.             machine.setHostname(hostname);  
  94.             group.addMachine(machine);  
  95.         }  
  96.   
  97.     }  

 2.6 DOM4J解析实现

    DOM4J也借助于XPath完成解析,起相应的Element等类就包装了JDOM中需要使用XPath类才能完成的路径选取功能。运行时也需要dom4j的实现jar包及jaxen jar包。

Java代码   收藏代码
  1. package parser.dom4j;  
  2.   
  3. import java.io.FileInputStream;  
  4. import java.util.ArrayList;  
  5. import java.util.List;  
  6.   
  7. import org.dom4j.Document;  
  8. import org.dom4j.Element;  
  9. import org.dom4j.io.SAXReader;  
  10.   
  11. import module.Group;  
  12. import module.Machine;  
  13. import parser.Constant;  
  14. import parser.MachineParser;  
  15.   
  16. /** 
  17.  *  
  18.  * Dom4JParser也结合XPath对XML节点进行解析 
  19.  * @author xuqingkang 
  20.  * 
  21.  */  
  22. public class Dom4JParser implements MachineParser {  
  23.   
  24.     SAXReader reader;  
  25.   
  26.     public Dom4JParser() {  
  27.         reader = new SAXReader();  
  28.     }  
  29.   
  30.     public List getGroupList(String xmlFilePath) throws Exception {  
  31.         FileInputStream stream = null;  
  32.         try {  
  33.             stream = new FileInputStream(xmlFilePath);  
  34.   
  35.             Document document = reader.read(stream);  
  36.             Element root = document.getRootElement();  
  37.   
  38.             return parse(root);  
  39.         } finally {  
  40.             try {  
  41.                 if (stream != null) {  
  42.                     stream.close();  
  43.                 }  
  44.             } catch (Exception e) {  
  45.             }  
  46.         }  
  47.     }  
  48.   
  49.     private List parse(Element root) {  
  50.         List groupList = new ArrayList();  
  51.         // 节点是从根节点开始select,因此xpath需要使用绝对路径(/打头)  
  52.         String xPath = "/" + Constant.ROOT + "/" + Constant.GROUPS + "/"  
  53.                 + Constant.GROUP;  
  54.         // 节点有多个,因此需要使用selectNodes  
  55.         List groupElements = root.selectNodes(xPath);  
  56.   
  57.         for (int i = 0; i < groupElements.size(); i++) {  
  58.             Element groupElement = groupElements.get(i);  
  59.   
  60.             String groupName = groupElement.attributeValue(Constant.NAME);  
  61.             Group group = new Group(groupName);  
  62.             parseGroup(groupElement, group);  
  63.             groupList.add(group);  
  64.         }  
  65.   
  66.         return groupList;  
  67.     }  
  68.   
  69.     private void parseGroup(Element groupElement, Group group) {  
  70.         // 一个节点下有多个节点,因此需要使用XPath.selectNodes  
  71.         List machineElements = groupElement  
  72.                 .selectNodes(Constant.MACHINE);  
  73.         for (int i = 0; i < machineElements.size(); i++) {  
  74.             Element machineElement = machineElements.get(i);  
  75.             String machineName = machineElement.attributeValue(Constant.NAME);  
  76.             String machineType = machineElement.attributeValue(Constant.TYPE);  
  77.             String ip = ((Element) machineElement.selectSingleNode(Constant.IP))  
  78.                     .getText();  
  79.             String hostname = ((Element) machineElement  
  80.                     .selectSingleNode(Constant.HOSTNAME)).getText();  
  81.   
  82.             Machine machine = new Machine(machineName);  
  83.             machine.setType(machineType);  
  84.             machine.setIp(ip);  
  85.             machine.setHostname(hostname);  
  86.             group.addMachine(machine);  
  87.         }  
  88.   
  89.     }  
  90.   
  91. }  

 2.7 StAX解析实现

    StAX是基于流的,在代码的结构上和SAX非常的相似,也可以借助栈来完成文件的解析。运行时也需要使用StAX的实现jar包。

Java代码   收藏代码
  1. package parser.stax;  
  2.   
  3. import java.io.FileReader;  
  4. import java.util.ArrayList;  
  5. import java.util.List;  
  6. import java.util.Stack;  
  7.   
  8. import javax.xml.stream.XMLInputFactory;  
  9. import javax.xml.stream.XMLStreamConstants;  
  10. import javax.xml.stream.XMLStreamException;  
  11. import javax.xml.stream.XMLStreamReader;  
  12. import javax.xml.stream.events.XMLEvent;  
  13.   
  14. import module.Group;  
  15. import module.Machine;  
  16. import parser.Constant;  
  17. import parser.MachineParser;  
  18.   
  19. /** 
  20.  * StAX api and RI could be downloaded from http://dist.codehaus.org/stax/jars/ 
  21.  *  
  22.  */  
  23.   
  24. public class STAXParser implements MachineParser {  
  25.   
  26.     List groupList = null;  
  27.     /** 
  28.      * 节点开始时压栈,结束时出栈 
  29.      */  
  30.     Stack groupStack = new Stack();  
  31.   
  32.     /** 
  33.      * 节点开始时压栈,结束时出栈 
  34.      */  
  35.     Stack machineStack = new Stack();  
  36.       
  37.     XMLInputFactory factory;  
  38.     public STAXParser() {  
  39.         factory = XMLInputFactory.newInstance();  
  40.     }  
  41.   
  42.     public List getGroupList(String xmlFilePath) throws Exception {  
  43.         XMLStreamReader reader = null;  
  44.         try {  
  45.             FileReader fileReader = new FileReader(xmlFilePath);  
  46.             reader = factory.createXMLStreamReader(fileReader);  
  47.   
  48.             process(reader);  
  49.         } finally {  
  50.             try {  
  51.                 reader.close();  
  52.             } catch (Exception e) {  
  53.             }  
  54.         }  
  55.         return groupList;  
  56.     }  
  57.   
  58.     /** 
  59.      * 循环处理,直到到达文件末尾 
  60.      * @param xmlr 
  61.      * @throws XMLStreamException 
  62.      */  
  63.     private void process(XMLStreamReader xmlr) throws XMLStreamException {  
  64.         xmlr.next();  
  65.         while (xmlr.getEventType() != XMLStreamConstants.END_DOCUMENT) {  
  66.             processEvent(xmlr);  
  67.             xmlr.next();  
  68.         }  
  69.     }  
  70.   
  71.     /** 
  72.      * 处理单个事件:节点开始、节点结束... 
  73.      * @param xmlr 
  74.      * @throws XMLStreamException 
  75.      */  
  76.     private void processEvent(XMLStreamReader xmlr) throws XMLStreamException {  
  77.         switch (xmlr.getEventType()) {  
  78.         case XMLEvent.START_ELEMENT:  
  79.             if (xmlr.getLocalName().equals(Constant.GROUPS)) {  
  80.                 groupList = new ArrayList();  
  81.             } else if (xmlr.getLocalName().equals(Constant.GROUP)) {  
  82.                 // parameter namespace is "".  
  83.                 String groupName = xmlr.getAttributeValue(null, Constant.NAME);  
  84.                 Group group = new Group(groupName);  
  85.                 groupStack.push(group);  
  86.             } else if (xmlr.getLocalName().equals(Constant.MACHINE)) {  
  87.                 String machineName = xmlr.getAttributeValue(null, Constant.NAME);  
  88.                 String machinType = xmlr.getAttributeValue(null, Constant.TYPE);  
  89.                 Machine machine = new Machine(machineName);  
  90.                 machine.setType(machinType);  
  91.   
  92.                 machineStack.push(machine);  
  93.             } else if (xmlr.getLocalName().equals(Constant.IP)) {  
  94.                 String ip = xmlr.getElementText();  
  95.                 machineStack.peek().setIp(ip);  
  96.             } else if (xmlr.getLocalName().equals(Constant.HOSTNAME)) {  
  97.                 String hostname = xmlr.getElementText();  
  98.                 machineStack.peek().setHostname(hostname);  
  99.             }  
  100.             break;  
  101.         case XMLEvent.END_ELEMENT:  
  102.             if (xmlr.getLocalName().equals(Constant.GROUP)) {  
  103.                 groupList.add(groupStack.pop());  
  104.             } else if (xmlr.getLocalName().equals(Constant.MACHINE)) {  
  105.                 groupStack.peek().addMachine(machineStack.pop());  
  106.             }  
  107.             break;  
  108.         case XMLEvent.CHARACTERS:  
  109.             break;  
  110.         case XMLEvent.SPACE:  
  111.             break;  
  112.         case XMLEvent.PROCESSING_INSTRUCTION:  
  113.             break;  
  114.         case XMLEvent.CDATA:  
  115.             break;  
  116.         case XMLEvent.COMMENT:  
  117.             break;  
  118.         case XMLEvent.ENTITY_REFERENCE:  
  119.             break;  
  120.         case XMLEvent.START_DOCUMENT:  
  121.             break;  
  122.         }  
  123.     }  
  124.   
  125. }  

 3,性能比较

    以上代码的解析逻辑基本一样,在此基础之上解析相同的文件,在我机器(i5三代CPU、8G DDR3内存)上得出的结果为

   3.1 采用前言的文件

      对同一个文件解析20000次耗时情况

解析技术 采样三次耗时 耗时平均值
SAX 3765 3738
  3690
  3760
DOM 4304 4498
  4682
  4510
JDOM 1.1.3 6928 7147
  7213
  7301
DOM4J 1.6 8513 8157
  8055
  7904
StAX 1.2 4586 4457
  4408
  4378

 

     3.2  小结

        从小文件的解析来看,SAX的性能占优,是DOM4J的2倍以上。DOM和StAX与SAX的差距都不是很大。在书写应用的时候,往往由于JDOM和DOM4J具有比较好用的API,所以很多应用总都优先使用它,在对性能要求比较高的场合,还是建议使用SAX。

4,测试程序

    如果在cmd下运行该测试程序请注意在classpath中添加附件给出的相关jar包。

Java代码   收藏代码
  1. import java.util.List;  
  2.   
  3. import module.Group;  
  4. import parser.MachineParser;  
  5. import parser.dom.JDKDomParser;  
  6. import parser.dom4j.Dom4JParser;  
  7. import parser.jdom.JDomParser;  
  8. import parser.sax.JDKSaxParser;  
  9. import parser.stax.STAXParser;  
  10.   
  11. public class ParseMain {  
  12.   
  13.     /** 
  14.      * @param args 
  15.      */  
  16.     public static void main(String[] args) {  
  17.         String xmlFile = "appGroups.xml";  
  18.         MachineParser parser = null;  
  19.         try {  
  20.             String parseType = "stax";  
  21.             int count = 20000;  
  22.             if(args.length > 0){  
  23.                 parseType = args[0];  
  24.             }else if(args.length > 1){  
  25.                 count = Integer.parseInt(args[1]);  
  26.             }  
  27.               
  28.             long startTime = System.currentTimeMillis();  
  29.             if("sax".equalsIgnoreCase(parseType)){  
  30.                 parser = new JDKSaxParser();  
  31.             }else if("dom4j".equalsIgnoreCase(parseType)){  
  32.                 parser = new Dom4JParser();  
  33.             }else if("jdom".equalsIgnoreCase(parseType)){  
  34.                 parser = new JDomParser();  
  35.             }else if("stax".equalsIgnoreCase(parseType)){  
  36.                 parser = new STAXParser();  
  37.             }else if("dom".equalsIgnoreCase(parseType)){  
  38.                 parser = new JDKDomParser();  
  39.             }else{  
  40.                 parser = new JDKDomParser();  
  41.             }  
  42.               
  43.             List groupList = null;  
  44.             for(int i =0;i
  45.                 if(i == (count -1)){  
  46.                     groupList = parser.getGroupList(xmlFile);  
  47.                 }else{  
  48.                     parser.getGroupList(xmlFile);  
  49.                 }  
  50.             }  
  51.               
  52.             for (Group group : groupList) {  
  53.                 System.out.println(group);  
  54.             }  
  55.             long endTime = System.currentTimeMillis();  
  56.             System.out.println("Exceute parse \"" + count + "\" times. and spent \"" + (endTime - startTime) + "\" milliseconds.");  
  57.         } catch (Exception e) {  
  58.             e.printStackTrace();  
  59.         }  
  60.     }  
  61.   
  62. }  

 

5,写在最后:

      1,以上的性能比较还不是很全面,只是以简单小文件的解析结果作为比较。

      2,本文只涉及XML的读取,不涉及XML的写入

      3,XML解析还有JAXB框架,在JDK1.6中已经包含了该框架,此框架使用也比较简单,本文未涉及

      4,SAX和StAX都基于事件机制,而JDOM和DOM4J借助了XPath

      5,本文也不涉及DTD和Schema校验

      6,本文所有代码和使用到的jar包都打包为附件,可直接下载查看。

你可能感兴趣的:(Java)