空間站

          北極心空

            BlogJava :: 首頁 :: 聯系 :: 聚合  :: 管理
            15 Posts :: 393 Stories :: 160 Comments :: 0 Trackbacks

          xml文件解析辦法

          xml是為了提高web數據交換量而出現的,雖然他現在web應用中并不廣泛,但是由于他的格式良好,經常被用做配置文件的格式。比如tomcat的主配置文件server.xml,web.xml等等。
           
          首先我們看一下需求。我們的目的主要是提取xml文件中的特定內容,又因為xml文件本身的格式良好,這種查詢是非常有規律的,非常有利于我們找到需要的信息。有時我們還可能把特定信息寫回xml中,但是這種需求并不是必需的,因為配置文件都不會太大,我們完全可以通過手工辦法進行修改。
           
          對xml進行解析的標準有兩種,sax以及dom。
          首先這兩種標準并不是針對java的,他們在各種語言環境下都可以實現。dom是真正的國際標準。sax是事實的標準,他不由任何商業組織維護,而是由一個非商業的組織在運作。就像iso7層模型和tcp/ip一樣,雖然sax不是正式的標準,但是一點不影響他在xml解析領域的地位。
           
          dom實現的原理是把整個xml文檔一次性讀出,放在一個樹型結構里。在需要的時候,查找特定節點,然后對節點進行讀或寫。他的主要優勢是實現簡單,讀寫平衡;缺點是比較占內存,因為他要把整個xml文檔都讀入內存,文件越大,這種缺點就越明顯。
           
          sax的實現方法和dom不同。他只在xml文檔中查找特定條件的內容,并且只提取需要的內容。這樣做占用內存小,靈活,正好滿足我們的需求。他的缺點就是寫,有些資料介紹了寫入的方法,但是我感覺這對本例沒有必要。后面主要講解用sax2.0實現xml文檔解析。
           
           
          首先講一下sax的工作流程,以下面的book.xml為例(不做dtd定義的驗證,如果有這方面需求,可以查更詳細的文檔)。
          <?xml version="1.0"?>
          <books>
                 <book type="computer">
                        <title>java 2</title>
                        <page>600</page>
                        <author>Jim</author>
                 </book>
                        <book type="fiction">
                        <title>fly to moon</title>
                        <page>300</page>
                        <author>Vernia</author>
                 </book>
          </books>
           
          1.我們需要注冊一個實現了sax標準的解析器,sun,java,apache等廠商和組織都實現了自己的解析器,大家可以直接拿過來用。
          2.然后告訴解析器,我們會用哪個xml解析程序來處理xml文檔。這個解析程序是由我們自己來實現的。
          3在解析開始時,解析器會觸發解析程序的startDocument()方法,告訴應用程序,文檔解析開始了。
          要注意以下幾點:
          1.區分解析器,解析程序的概念。
          2.sax實現是事件驅動的,由解析器觸發應用程序,而不是由應用程序來調用解析器。這和ui里的Actionlistener實現差不多。
          3.startDocument()方法是由ContentHandler接口定義的,我們必須要實現他。xml解析程序就是用來實現這些方法的。為什么要這么做?因為sax不會定義在接收到方法觸發后,會采取什么動作。只有我們自己才知道在解析的過程中,我們會做什么。不明白沒有關系,再往下看。
          4.當遇到<books>后,解析器會觸發解析程序的startElement()方法,告訴應用程序,我遇到一個開始的標簽。這個startElement()方法也是由ContentHandler接口定義的,他只是提醒應用程序他遇到一個標簽的開始,至于是什么標簽,他不知道,也不想知道。而由xml解析程序實現了的startElement()方法,功能就大了。比如我們可以判斷這個標簽的內容是什么,如果是books,好,正是我們需要的,要記到內存里;如果不是,放棄,繼續往下走。
          5.過了<books>后,解析器會觸發解析程序的characters()方法,告訴應用程序,我遇到了標簽的內容。同樣的原理,由xml解析程序實現了的characters()方法會處理這個內容。當然了如果是我們需要的,就留下;如果不是就放棄。在這個例子里,<books>后面是空格,沒有實際價值。
          6.再往下遇到了<book type="computer">標簽,同樣觸發的是startElement()方法。以此類推,在標簽結束時,會觸發endElement()方法,在文檔結束時會觸發endDocument()方法。至于每次觸發一個方法后,產生什么動作,都是由我們的解析程序來控制的。
           
          下面是一個程序例子,解析book.xml文件,把是book,并且類型是fiction的內容挑出來。
           
          文件XmlParseAction,接收xml文件輸入,調用xml解析程序,并返回結果。
          package myb.hi.sample.action;
           
          import java.io.File;
           
          import javax.servlet.http.HttpServletRequest;
          import javax.servlet.http.HttpServletResponse;
          import org.apache.struts.action.ActionForm;
          import org.apache.struts.action.ActionForward;
          import org.apache.struts.action.ActionMapping;
          import org.apache.struts.actions.DispatchAction;
          import org.xml.sax.*;
          import org.apache.xerces.jaxp.*;
          import myb.hi.sample.form.XmlFileForm;
          import myb.hi.sample.business.*;
          import javax.servlet.*;
           
          public class XmlParseAction extends DispatchAction{
           
                 /**
                  * Accept the jsp request, parset the xml file
                  * @param mapping
                  * @param form
                  * @param request
                  * @param response
                  * @return ActionForward
                  */
                 public ActionForward xmlParse(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception{
                        log.info("Start xml parse: XmlParseAction.xmlParse()");
                        XmlFileForm userForm=(XmlFileForm)form;
                        try{
                               //注冊一個解析器,我用的是apache xerces的,所以導入了org.apache.xerces.jaxp.*
                               XMLReader xmlReader=new SAXParserFactoryImpl().newSAXParser().getXMLReader();
                               //聲明一個XmlParseBusiness類,這個類就是我們自己的xml解析程序
          XmlParseBusiness xmlParseBusiness=new XmlParseBusiness();
          //把我們自己的解析程序注冊到解析器,告訴解析器誰來接收事件
                               xmlReader.setContentHandler(xmlParseBusiness);
                               //解析文件
                               // userForm.getFileName()是指定的xml文件名稱,本例子中就是book.xml
                               // getPath(servlet)方法找到book.xml的路徑
                               //.toURL().toString()把文件轉換成url形式
                               //parse()方法的參數為inputSource,可以是字符流,字節流或文件的url字符串,所以必須要把以上幾種轉換成inputSource
                               xmlReader.parse(new File(getPath(servlet)+"/WEB-INF/classes/"+userForm.getFileName()).toURL().toString());
                              
                               request.setAttribute("iValue",xmlParseBusiness.getI());
                               request.setAttribute("bookList",xmlParseBusiness.getBookList());
                               log.info("End xml parse: XmlParseAction.xmlParse()");
                        }catch(Exception ex){
                               System.out.println("Action Exception: xmlParse, caused by: "+ex);
                        }
                        return mapping.findForward("xmlParseResult");
                 }
           
                 /**
                  * Return the absolute path of the servlet
                  */
                 //這個方法就是查找此應用程序的絕對路徑,供解析用,因為sax不會自動識別上下文路徑
                 private String getPath(Servlet servlet){
                        String strPath=servlet.getServletConfig().getServletContext().getRealPath("");
                        return strPath;
                 }
           
          }
           
           
          文件XmlParseBusiness,接收解析事件,查找符合book,類型是fiction的內容
           
          package myb.hi.sample.business;
           
          import java.util.*;
           
          import org.xml.sax.Attributes;
          import org.xml.sax.SAXException;
          import org.xml.sax.helpers.AttributesImpl;
          import org.xml.sax.helpers.DefaultHandler;
          // DefaultHandler是一個實現了ContentHander等接口的類,繼承這個類就不用挨個實現接口了 
          public class XmlParseBusiness extends DefaultHandler{
                 //定義整形變量i,存放共有幾本書
                 private int i=0;
                 //接收標簽內容,因為characters()方法是以字符流的形式接收內容,如果用string,有可能造成內容缺失
                 private StringBuffer xmlContent=new StringBuffer();
                 //聲明一個mapp對象,并不做初始化
                 private HashMap bookMap=null;
                 //定義list對象,存放書的列表
                 private List bookList=new ArrayList();
                 //定義堆棧,存放上下文信息。在解析過程中,我們需要記錄一些信息,比如我現在是在<book></book>之間,等等。堆棧是一個很好的辦法,他采用后進先出,后面定義的方法,有他的實現
                 private Stack context=new Stack();
                
                 /**
                  * Default Constructor
                  */
                 public XmlParseBusiness(){
                       
                 }
                
                 /**
                  * Start Document
                  */
                 public void startDocument(){
                       
                 }
                
                 /**
                  * End Document
                  */
                 public void endDocument(){
                       
                 }
                
                 /**
                  * Start Element
                  * @param uri
                  * @param localName
                  * @param qName
                  * @param attr
                  * @return void
                  */
                 public void startElement(String uri, String localName,String qName,Attributes attribute) throws SAXException{
                        //聲明一個ElementDetails類的實例,這個類存放的就是標簽信息,目的是放到堆棧中
                        ElementDetails elem=new ElementDetails(uri, localName,qName, attribute);
                        //把信息推入堆棧
                        context.push(elem);
                        //如果標簽是<book>,就執行下面代碼
                        if(qName.equals("book")){
                               //如果book類型是fiction,就執行下面代碼
                               if(isFictionBook()){
                                      //初始化bookMap為一個map對象實例
                                      bookMap=new HashMap();
                               }
                               //給i自增,代表又多了一本書
                               i++;
                        }
                        //給stringbuffer清空,以便接收新內容
                        xmlContent.setLength(0);
                 }
                
                 /**
                  * End Element
                  */
                 public void endElement(String uri, String localName,String qName) throws SAXException{
          //根據上下文做判斷,如果還在<book></book>之間
                        if(isBook()){
                               //如果是</title>
                               if(qName.equals("title")){
                                      //把書名內容放到map里
                                      bookMap.put("title",xmlContent.toString());
                               //如果是</page>
                               }else if(qName.equals("page")){
                                      //把書的頁數放到map里
                                      bookMap.put("page",xmlContent.toString());
                               //如果是</author>
                               }else if(qName.equals("author")){
                                      //把作者名稱放到map里
                                      bookMap.put("author",xmlContent.toString());
                            //如果是</book>
                               }else if(qName.equals("book")){
                                      //說明book標簽結束了,把整本書放到列表里
                                      bookList.add(bookMap);
                               }
                        }
                        //給stringbuffer清空,以便接收新內容
                        xmlContent.setLength(0);
                        //把最后進來的對象彈出堆棧,因為他的標簽已經結束,沒有再存在的必要了(后進先出
                        context.pop();
                 }
                
                 /**
                  * Get i value
                  */
                 public int getI(){
                        return i;
                 }
                
                 /**
                  *Handle the context between the element
                  *@param ch[]
                  *@param start
                  *@param length
                  *@return void
                  */
                  public void characters (char ch[], int start, int length) throws SAXException{
          //把標簽內容存到一個stringbuffer對象里,以備處理
                         xmlContent.append(ch,start,length);
                  }
                
                 /**
                 * Get strA value
                 */
                 public String getContent(){
                        return xmlContent.toString();
                 }
                
                 /**
                  * Return bookList
                  */
                 public List getBookList(){
                        return bookList;
                 }
                
                 /**
                  * Define a internal Class, for transfor the element details
                  */
                 //定義一個內部類,接收標簽元素信息,供堆棧用
                 private class ElementDetails {
                        private String uri;
                        private String localName;
                        private String qName;
                        private Attributes attribute;
                       
                        /*
                         * Defalut Constructor
                         */
                        public ElementDetails(String uri, String localName,String qName,Attributes attribute){
                               this.uri=uri;
                               this.localName=localName;
                               this.qName=qName;
                               //注意Attributes是一個接口,所以要把他轉化為一個AttributesImpl對象
                               this.attribute=new AttributesImpl(attribute);
                        }
           
           
                        public Attributes getAttribute() {
                               return attribute;
                        }
           
           
                        public void setAttribute(Attributes attribute) {
                               this.attribute = new AttributesImpl(attribute);
                        }
           
           
                        public String getLocalName() {
                               return localName;
                        }
           
                        public void setLocalName(String localName) {
                               this.localName = localName;
                        }
           
                        public String getQName() {
                               return qName;
                        }
           
                        public void setQName(String name) {
                               qName = name;
                        }
           
                        public String getUri() {
                               return uri;
                        }
           
                        public void setUri(String uri) {
                               this.uri = uri;
                        }    
                 }
                
                 /**
                  * Estimate the element content, if it's 'book', return true, otherwise, false
                  */
                 //利用堆棧,判斷是否還在<book></book>之間
                 private Boolean isBook(){
                        //判斷堆棧里對象數目,并做循環
                        for(int p=context.size()-1;p>=0;p--){
                               //把位置p出的對象取出來,是一個ElementDetails類的實例
                               ElementDetails elem=(ElementDetails)context.elementAt(p);
                               //如果這個標簽的信息是<book>,返回true,不用再往下循環了。因為</book>后,會被彈出堆棧,所以不會有2個<book>在堆棧里。除非xml不規范,有相同的標簽嵌套出現,像<book><book></book></book>這樣,但是在這里因為后進先出的原則不會出問題,相反程序里的其他判斷就要出亂子了
                               if(elem.getQName().equals("book")){
                                      return true;
                               }
                        }
                        return false;
                 }
           
                 /**
                  * Estimate the element content, if it's a "fiction book", return true, otherwise, false
                  */
                 private Boolean isFictionBook(){
                        for(int p=context.size()-1;p>=0;p--){
                               ElementDetails elem=(ElementDetails)context.elementAt(p);
                               if(elem.getQName().equals("book") && elem.getAttribute().getValue("type").equals("fiction")){
                                      return true;
                               }
                        }
                        return false;
                 }
          }
           
          注意:上面程序只實現了ContentHandler接口的部分方法。并且對帶dtd驗證的xml解析,以及錯誤處理沒有做講解和實例,感興趣的朋友可以自己去參考文檔。不過對于解析簡單的xml配置文檔,這些也足夠了。
           
          Sax的api包含在j2se的api文擋中,在org.xml.sax包里。apache的xerces包,ibm,oracle等廠商發行的xml解析器包都會包含他。
          Xerces是apache的子項目,專門做xml解析,他不僅包含了sax實現,也包含了dom實現。
          最后講一下sax1和sax2的幾點差別:
          1. sax2中,用XMLReader代替了Parser
          2. sax2支持namespace
          3. sax2中用ContentHandler代替了DocumentHandler
          4. sax2 DefaultHandler代替了HandlerBase



           

          Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1486724

          posted on 2007-04-06 13:07 蘆葦 閱讀(3948) 評論(0)  編輯  收藏 所屬分類: JAVA其他
          主站蜘蛛池模板: 双柏县| 思南县| 红原县| 江北区| 吉木萨尔县| 佳木斯市| 韶关市| 和田市| 丘北县| 安图县| 始兴县| 团风县| 宣汉县| 高阳县| 宁城县| 新巴尔虎左旗| 乡城县| 泰兴市| 军事| 韩城市| 全州县| 海阳市| 天峻县| 咸宁市| 平谷区| 通许县| 木兰县| 开封县| 缙云县| 汨罗市| 广灵县| 资兴市| 叙永县| 海兴县| 庆安县| 吕梁市| 文昌市| 阳信县| 鄂温| 黄浦区| 内丘县|