空间站

北极心空

  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 芦苇 阅读(3928) 评论(0)  编辑  收藏 所属分类: JAVA其他

只有注册用户登录后才能发表评论。


网站导航: