sax解析中事件处理器要注意的问题

来源:互联网 发布:种植牙 知乎 疼 编辑:程序博客网 时间:2024/04/20 03:04

前段时间,在写一段解析xml的代码时发现了一个问题。我用的是SAX,这确实是很简单好用的一个东东。我们只需要继承DefaulHandler ,实现其中的方法即可。但是我们要注意到,其中的 characters 方法在解析一个节点的时候是可能会执行多次的。

假设我们的 persons.xml 文件如下:

[html] view plain copy
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <persons>  
  3. <person>  
  4. <name>Tom Jeff</name>  
  5. <sex>M</sex>  
  6. <age>20</age>  
  7. </person>  
  8. <person>  
  9. <name>Cater</name>  
  10. <sex>M</sex>  
  11. <age>23</age>  
  12. </person>  
  13. <person>  
  14. <name>Susan</name>  
  15. <sex>F</sex>  
  16. <age>19</age>  
  17. </person>  
  18. <person>  
  19. <name>Lily</name>  
  20. <sex>F</sex>  
  21. <age>22</age>  
  22. </person>  
  23. </persons>  

这段xml 很简单,我们要做的事情也很简单,只需要把其中的person 解析出来放入一个list中即可。像下面的这种写法是可能会有问题的:

[java] view plain copy
  1. /**   
  2.  * @Title: MySaxHandler.java 
  3.  * @Description: TODO 
  4.  * @author ThinkPad 
  5.  * @version 1.0 
  6.  * @date 2014年7月13日 
  7.  */  
  8. package com.sax.example;  
  9.   
  10. import java.util.ArrayList;  
  11. import java.util.List;  
  12.   
  13. import org.xml.sax.Attributes;  
  14. import org.xml.sax.SAXException;  
  15. import org.xml.sax.helpers.DefaultHandler;  
  16.   
  17. /** 
  18.  * @author ThinkPad 
  19.  * 
  20.  */  
  21. public class MySaxHandler1 extends DefaultHandler {  
  22.   
  23.     /** 
  24.      *  xml 解析结果 
  25.      */  
  26.     public static List<Person> personList;  
  27.       
  28.     private Person person;  
  29.       
  30.     private String node;  
  31.       
  32.     private boolean flag = false;   
  33.       
  34.     public void startDocument () throws SAXException {    
  35.         //开始解析文档    
  36.         super.startDocument();  
  37.         personList = new ArrayList<Person>();  
  38.     }    
  39.     
  40.     public void endDocument () throws SAXException {    
  41.         //文档解析结束    
  42.         super.endDocument();  
  43.     }    
  44.     
  45.     public void startElement (String uri, String localName, String qName, Attributes attributes) throws SAXException {    
  46.         //开始解析节点    
  47.         super.startElement(uri, localName, qName, attributes);  
  48.         flag = true;   
  49.         if( qName.equals("person")){  
  50.             person = new Person();  
  51.         }  
  52.         node = qName;  
  53.     }    
  54.         
  55.     public void characters (char[] ch, int start, int length) throws SAXException {    
  56.         //保存节点内容    
  57.         super.characters(ch, start, length);  
  58.           
  59.         if(!flag) {    
  60.             return;    
  61.         }    
  62.           
  63.         String s = new String(ch, start, length);     
  64.         switch (node) {  
  65.             case "name":  
  66.                   person.setName(s);  
  67.                  break;  
  68.             case "sex":  
  69.                  person.setSex(s);  
  70.                  break;    
  71.             case "age":  
  72.                  person.setAge(s);  
  73.                  break;  
  74.             default:  
  75.                 break;  
  76.         }     
  77.     }    
  78.         
  79.     public void endElement (String uri, String localName, String qName) throws SAXException {    
  80.         //结束解析节点    
  81.         super.endElement(uri, localName, qName);  
  82.         flag = false;   
  83.         if( qName.equals("person")){  
  84.             personList.add(person);  
  85.         }  
  86.     }    
  87. }  

当然,就解析上面的那段xml ,这是没有问题的。然而让人难堪的是,我想改一下xml ,在某些节点加上 \r \n \t 之类的字符,来证实这样解析会丢失部分数据,却没有成功。它一直工作的很好??? 很多人在博文里说“,当遇到内容中有回车,\t等等内容时,characters 方法有可能会执行多次”,看来在这种情况下也未必一定会执行多次。这是随机的? 总之,我在生产环境中,使用类似上面的解析方法确实遇到了截断节点数据的问题。因此,我确信,上面的写法是有问题的。

正确的、合理的写法如下,用一个StringBuilder 在characters 方法中拼接数据,在 endElement 方法中填充数据。

[java] view plain copy
  1. /**   
  2.  * @Title: MySaxHandler.java 
  3.  * @Description: TODO 
  4.  * @author ThinkPad 
  5.  * @version 1.0 
  6.  * @date 2014年7月13日 
  7.  */  
  8. package com.sax.example;  
  9.   
  10. import java.util.ArrayList;  
  11. import java.util.List;  
  12.   
  13. import org.xml.sax.Attributes;  
  14. import org.xml.sax.SAXException;  
  15. import org.xml.sax.helpers.DefaultHandler;  
  16.   
  17. /** 
  18.  * @author ThinkPad 
  19.  * 
  20.  */  
  21. public class MySaxHandler extends DefaultHandler {  
  22.   
  23.     /** 
  24.      *  xml 解析结果 
  25.      */  
  26.     public static List<Person> personList;  
  27.       
  28.     private Person person;  
  29.       
  30.     private String node;  
  31.       
  32.     private StringBuilder sb;  
  33.       
  34.     private boolean flag = false;  
  35.       
  36.     public void startDocument () throws SAXException {    
  37.         //开始解析文档    
  38.         super.startDocument();  
  39.         personList = new ArrayList<Person>();  
  40.     }    
  41.     
  42.     public void endDocument () throws SAXException {    
  43.         //文档解析结束    
  44.         super.endDocument();  
  45.     }    
  46.     
  47.     public void startElement (String uri, String localName, String qName, Attributes attributes) throws SAXException {    
  48.         //开始解析节点    
  49.         super.startElement(uri, localName, qName, attributes);  
  50.         flag = true;  
  51.         if( qName.equals("person")){  
  52.             person = new Person();  
  53.         }  
  54.         node = qName;  
  55.         sb = new StringBuilder();  
  56.     }    
  57.         
  58.     public void characters (char[] ch, int start, int length) throws SAXException {    
  59.         //保存节点内容    
  60.         super.characters(ch, start, length);  
  61.         if(!flag) {    
  62.             return;    
  63.         }    
  64.         sb.append(new String(ch, start, length) );  
  65.           
  66.     }    
  67.         
  68.     public void endElement (String uri, String localName, String qName) throws SAXException {    
  69.         //结束解析节点    
  70.         super.endElement(uri, localName, qName);  
  71.         flag = false;   
  72.         if( qName.equals("person")){  
  73.             personList.add(person);  
  74.         }  
  75.         String s = sb.toString();  
  76.         switch (node) {  
  77.             case "name":  
  78.                   person.setName(s);  
  79.                  break;  
  80.             case "sex":  
  81.                  person.setSex(s);  
  82.                  break;    
  83.             case "age":  
  84.                  person.setAge(s);  
  85.                  break;  
  86.             default:  
  87.                 break;  
  88.        }          
  89.     }    
  90. }