XML(DOM)解析

来源:互联网 发布:厌胜术 知乎 编辑:程序博客网 时间:2024/05/22 17:01
 

XML解析方式分两种:dom和sax

Dom(Document Object Model,即文档对象模型)是W3C组织推荐的处理XML的一种方式。

SAX(Simple API for XML)不是官方标准,但它是XML社区事实上的标准,几乎所有的XML解析器都支持它。

XML解析器

Crimson、Xerces、Aelfred2

XML的解析开发包有:

Jaxp、Jdom、dom4j

JAXP:开发包是J2SE的一部分,它由javax.xml、org.w3c.dom、org.xml.sax 包及其子包组成

在Javax.xml.parsers 包中,定义了几个工厂类,程序员调用这些工厂类,可以得到对XML文档进行解析的DOM或SAX的解析器对象。

使用JAXP进行DOM解析

Javax.xml.parsers 包中的DocumentBuilderFactory 用于创建DOM模式的解析器对象,DocumentBuilderFactory是一个抽象工厂类,它不能直接实例化,但该类提供了一个newlnstance 方法,这个方法会根据本地平台默认安装的解析器,自动创建一个工厂的对象并返回。

获得JAXP中的DOM解析器

l  调用DocumentBuilderFactory.newlnstance()方法得到创建DOM解析器的工厂。

l  调用工厂对象的newDocumentBuilder方法得到DOM解析器对象。

l  调用DOM解析器对象的parse()方法解析XML文档,得到代表整个文档的

l  Document对象,进行可以利用DOM特性对整个XML文档进行操作了。

DOM模型(document object model)

DOM解析器在解析XML文档时,会把文档中的所有元素,按照期出现的层次关系,解析成为一个个Node对象(节点)。

在dom中,节点之间关系如下:

l  位于一个节点之上的节点是该节点的父节点(parent)

l  一个节点之下的节点是该节点的子节点(children)

l  同一层次,具有相同父节点的节点是兄弟节点(sibling)

l  一个节点的下一层节点集合是节点后代(descendant)

l  父、祖父节点及所有位于节点上面的,都是节点的祖先(ancestor)

Node对象

Node对象提供了一系列常量来代表节点的类型,当开发人员获得某个Node类型后,就可以把Node节点转换成相应的节点对象(Node的子类对象),以便于调用其特有的方法。

Node对象提供了相应的方法去获取他的父节点或子节点。编程人员通过这些方法就可以读取整个XML文档的内容、或添加、修改、删除XML文档内容了。

下面是一个程序例子,解析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配置文档,这些也足够了。

 

原创粉丝点击