欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Java中使用DOM和SAX解析XML文件的方法示例

程序员文章站 2024-03-07 20:41:09
dom4j介绍   dom4j的项目地址:http://sourceforge.net/projects/dom4j/?source=directory   d...

dom4j介绍
  dom4j的项目地址:http://sourceforge.net/projects/dom4j/?source=directory

  dom4j是一个简单的开源库,用于处理xml、 xpath和xslt,它基于java平台,使用java的集合框架,全面集成了dom,sax和jaxp。

 

dom4j的使用
  下载了dom4j项目之后,解压缩,将其jar包(我的当前版本叫做dom4j-1.6.1.jar)加入class path下面。

  (properties->java build path -> add external jars...)。

  之后就可以使用其提供的api进行编程。

 

程序实例1
  第一个程序,用java代码生成xml文档,代码如下:

package com.example.xml.dom4j;

import java.io.fileoutputstream;
import java.io.filewriter;

import org.dom4j.document;
import org.dom4j.documenthelper;
import org.dom4j.element;
import org.dom4j.io.outputformat;
import org.dom4j.io.xmlwriter;

/**
 * dom4j框架学习 使用dom4j框架创建xml文档并输出保存
 * 
 */
public class dom4jtest1
{

  public static void main(string[] args) throws exception
  {
    // 第一种方式:创建文档,并创建根元素
    // 创建文档:使用了一个helper类
    document document = documenthelper.createdocument();

    // 创建根节点并添加进文档
    element root = documenthelper.createelement("student");
    document.setrootelement(root);

    // 第二种方式:创建文档并设置文档的根元素节点
    element root2 = documenthelper.createelement("student");
    document document2 = documenthelper.createdocument(root2);

    // 添加属性
    root2.addattribute("name", "zhangsan");
    // 添加子节点:add之后就返回这个元素
    element helloelement = root2.addelement("hello");
    element worldelement = root2.addelement("world");

    helloelement.settext("hello text");
    worldelement.settext("world text");

    // 输出
    // 输出到控制台
    xmlwriter xmlwriter = new xmlwriter();
    xmlwriter.write(document);

    // 输出到文件
    // 格式
    outputformat format = new outputformat("  ", true);// 设置缩进为4个空格,并且另起一行为true
    xmlwriter xmlwriter2 = new xmlwriter(
        new fileoutputstream("student.xml"), format);
    xmlwriter2.write(document2);

    // 另一种输出方式,记得要调用flush()方法,否则输出的文件中显示空白
    xmlwriter xmlwriter3 = new xmlwriter(new filewriter("student2.xml"),
        format);
    xmlwriter3.write(document2);
    xmlwriter3.flush();
    // close()方法也可以

  }
}

 

  程序console输出:

<?xml version="1.0" encoding="utf-8"?>
<student/>

  生成的一个xml文档:

<?xml version="1.0" encoding="utf-8"?>

<student name="zhangsan">
  <hello>hello text</hello>
  <world>world text</world>
</student>

 

程序实例2
  程序实例2,读入xml文档并分析,将其内容输出。

  首先,待分析的文档如下:

<?xml version="1.0" encoding="utf-8"?>

<students name="zhangsan">
  <hello name="lisi">hello text1</hello>
  <hello name="lisi2">hello text2</hello>
  <hello name="lisi3">hello text3</hello>
  <world name="wangwu">world text1</world>
  <world name="wangwu2">world text2</world>
  <world >world text3</world>
</students>
 

package com.example.xml.dom4j;

import java.io.file;
import java.util.iterator;
import java.util.list;

import javax.xml.parsers.documentbuilder;
import javax.xml.parsers.documentbuilderfactory;

import org.dom4j.document;
import org.dom4j.element;
import org.dom4j.io.domreader;
import org.dom4j.io.saxreader;

/**
 * dom4j框架学习: 读取并解析xml
 * 
 * 
 */
public class dom4jtest2
{
  public static void main(string[] args) throws exception
  {
    saxreader saxreader = new saxreader();

    document document = saxreader.read(new file("students.xml"));

    // 获取根元素
    element root = document.getrootelement();
    system.out.println("root: " + root.getname());

    // 获取所有子元素
    list<element> childlist = root.elements();
    system.out.println("total child count: " + childlist.size());

    // 获取特定名称的子元素
    list<element> childlist2 = root.elements("hello");
    system.out.println("hello child: " + childlist2.size());

    // 获取名字为指定名称的第一个子元素
    element firstworldelement = root.element("world");
    // 输出其属性
    system.out.println("first world attr: "
        + firstworldelement.attribute(0).getname() + "="
        + firstworldelement.attributevalue("name"));

    system.out.println("迭代输出-----------------------");
    // 迭代输出
    for (iterator iter = root.elementiterator(); iter.hasnext();)
    {
      element e = (element) iter.next();
      system.out.println(e.attributevalue("name"));

    }

    system.out.println("用domreader-----------------------");
    documentbuilderfactory dbf = documentbuilderfactory.newinstance();
    documentbuilder db = dbf.newdocumentbuilder();
    // 注意要用完整类名
    org.w3c.dom.document document2 = db.parse(new file("students.xml "));

    domreader domreader = new domreader();

    // 将jaxp的document转换为dom4j的document
    document document3 = domreader.read(document2);

    element rootelement = document3.getrootelement();

    system.out.println("root: " + rootelement.getname());

  }

}


  代码运行后输出:

root: students
total child count: 6
hello child: 3
first world attr: name=wangwu
迭代输出-----------------------
lisi
lisi2
lisi3
wangwu
wangwu2
null
用domreader-----------------------
root: students


sax解析xml
下面是sax实现实体解析的步骤
//下面使用xmlreader 来解析
(一)第一步:新建一个工厂类saxparserfactory,代码如下:
saxparserfactory factory = saxparserfactory.newinstance();
(二)第二步:让工厂类产生一个sax的解析类saxparser,代码如下:
saxparser parser = factory.newsaxparser();
(三)第三步:从saxpsrser中得到一个xmlreader实例,代码如下:
xmlreader reader = parser.getxmlreader();
(四)第四步:把自己写的handler注册到xmlreader中,一般最重要的就是contenthandler,代码如下:
reader.setcontenthandler(this);
(五)第五步:将一个xml文档或者资源变成一个java可以处理的inputstream流后,解析正式开始,代码如下:
reader.parse(new inputsource(is));
 
 
//下面使用saxparser来解析
(一)第一步:新建一个工厂类saxparserfactory,代码如下:
saxparserfactory factory = saxparserfactory.newinstance();
(二)第二步:让工厂类产生一个sax的解析类saxparser,代码如下:
saxparser parser = factory.newsaxparser();
(三)第三步:将一个xml文档或者资源变成一个java可以处理的inputstream流后,解析正式开始,代码如下:
parser.parse(is,this);
估计大家都看到了contenthandler ,下面具体的讲下
解析开始之前,需要向xmlreader/saxparser 注册一个contenthandler,也就是相当于一个事件监听器,在contenthandler中定义了很多方法
//设置一个可以定位文档内容事件发生位置的定位器对象
public void setdocumentlocator(locator locator)
 
//用于处理文档解析开始事件
public void startdocument()throws saxexception
 
//处理元素开始事件,从参数中可以获得元素所在名称空间的uri,元素名称,属性类表等信息
public void startelement(string namespacesuri , string localname , string qname , attributes atts) throws saxexception
 
//处理元素结束事件,从参数中可以获得元素所在名称空间的uri,元素名称等信息
public void endelement(string namespacesuri , string localname , string qname) throws saxexception
 
//处理元素的字符内容,从参数中可以获得内容
public void characters(char[] ch , int start , int length)  throws saxexception
顺便介绍下xmlreader中的方法。
//注册处理xml文档解析事件contenthandler
public void setcontenthandler(contenthandler handler)
 
//开始解析一个xml文档
public void parse(inputsorce input) throws saxexception

大概的讲的差不多了  接下来开始讲解解析的步骤
我们还是用上一章的代码
首先 我们创建一个person类  用来存储用户的信息

package com.example.demo; 
 
import java.io.serializable; 
 
public class person implements serializable { 
 
  /** 
   * 
   */ 
  private static final long serialversionuid = 1l; 
  private string _id; 
  private string _name; 
  private string _age; 
 
  public string get_id() { 
    return _id; 
  } 
 
  public void set_id(string _id) { 
    this._id = _id; 
  } 
 
  public string get_name() { 
    return _name; 
  } 
 
  public void set_name(string _name) { 
    this._name = _name; 
  } 
 
  public string get_age() { 
    return _age; 
  } 
 
  public void set_age(string _age) { 
    this._age = _age; 
  } 
} 

接下来  我们要实现一个contenthandler 用来解析xml
实现一个contenthandler 一般需要下面几个步骤
1、声明一个类,继承defaulthandler。defaulthandler是一个基类,这个类里面简单实现了一个contenthandler。我们只需要重写里面的方法即可。
2、重写 startdocument() 和 enddocument(),一般将正式解析之前的初始化放到startdocument()里面,收尾的工作放到enddocument()里面。
3、重写startelement(),xml解析器遇到xml里面的tag时就会调用这个函数。经常在这个函数内是通过对localname的值进行判断而操作一些数据。
4、重写characters()方法,这是一个回调方法。解析器执行完startelement()后,解析节点的内容后就会执行这个方法,并且参数ch[]就是节点的内容。
5、重写endelement()方法,这个方法与startelement()相对应,解析完一个tag节点后,执行这个方法,解析一个tag后,调用这个处理还原和清除相关信息
首先   新建一个类 继承defaulthandler 并重写以下几个方法

public class sax_parserxml extends defaulthandler { 
 
  /** 
   * 当开始解析xml文件的声明的时候就会触发这个事件, 可以做一些初始化的工作 
   * */ 
  @override 
  public void startdocument() throws saxexception { 
    // todo auto-generated method stub 
    super.startdocument(); 
     
  } 
 
  /** 
   * 当开始解析元素的开始标签的时候,就会触发这个事件 
   * */ 
  @override 
  public void startelement(string uri, string localname, string qname, 
      attributes attributes) throws saxexception { 
    // todo auto-generated method stub 
    super.startelement(uri, localname, qname, attributes); 
  } 
 
  /** 
   * 当读到文本元素的时候要触发这个事件. 
   * */ 
  @override 
  public void characters(char[] ch, int start, int length) 
      throws saxexception { 
    // todo auto-generated method stub 
    super.characters(ch, start, length); 
  } 
 
  /** 
   * 当读到结束标签的时候 就会触发这个事件 
   * */ 
  @override 
  public void endelement(string uri, string localname, string qname) 
      throws saxexception { 
    // todo auto-generated method stub 
    super.endelement(uri, localname, qname); 
  } 
 
} 

首先  我们创建一个list  用来保存解析出来的person数据

list<person> persons; 

但是?在哪里初始化呢?我们可以在startdocument()里面初始化,因为当开始解析xml文件的声明的时候就会触发这个事件所以放在这里比较合适

/** 
 * 当开始解析xml文件的声明的时候就会触发这个事件, 可以做一些初始化的工作 
 * */ 
@override 
public void startdocument() throws saxexception { 
  // todo auto-generated method stub 
  super.startdocument(); 
  // 初始化list 
  persons = new arraylist<person>(); 
} 

接下来  就要开始解析了  

/** 
   * 当开始解析元素的开始标签的时候,就会触发这个事件 
   * */ 
  @override 
  public void startelement(string uri, string localname, string qname, 
      attributes attributes) throws saxexception { 
    // todo auto-generated method stub 
    super.startelement(uri, localname, qname, attributes); 
 
    // 如果读到是person标签 开始存储 
    if (localname.equals("person")) { 
      person = new person(); 
      person.set_id(attributes.getvalue("id")); 
    } 
    curnode = localname; 
  } 

 上面的代码中  localname表示当前解析到的元素名

//步骤 
//1.判断是否是person元素 
//2.创建新的person对象 
//3.获取id 添加到person对象中 
curnode 用来保存当前的元素名 在characters中会使用到
/** 
   * 当读到文本元素的时候要触发这个事件. 
   * */ 
  @override 
  public void characters(char[] ch, int start, int length) 
      throws saxexception { 
    // todo auto-generated method stub 
    super.characters(ch, start, length); 
 
    if (person != null) { 
      //取出目前元素对应的值 
      string txt = new string(ch, start, length); 
      //判断元素是否是name 
      if (curnode.equals("name")) { 
        //将取出的值添加到person对象 
        person.set_name(txt); 
      } else if (curnode.equals("age")) { 
        person.set_age(txt); 
      } 
    } 
  } 

接下来是介绍标签结束的时候需要做的事情

/** 
 * 当读到结束标签的时候 就会触发这个事件 
 * */ 
@override 
public void endelement(string uri, string localname, string qname) 
    throws saxexception { 
  // todo auto-generated method stub 
  super.endelement(uri, localname, qname); 
 
  // 如果是 并且person不为空,添加到list 
  if (localname.equals("person") && person != null) { 
    persons.add(person); 
    person = null; 
  } 
 
  curnode = ""; 
} 

解析的事情结束了  大概流程就是
1.一个元素开始时    会调用startelement方法
2.接下来会调用到characters方法,可以用来获取元素的值
3.一个元素结束时    会调用到endelement方法
解析结束之后  我们需要写一个方法  用来获取解析后保存的list

public list<person> readxml(inputstream is) { 
 
    saxparserfactory factory = saxparserfactory.newinstance(); 
    try { 
      saxparser parser = factory.newsaxparser(); 
 
      // 第一种方法 
      // parser.parse(is, this); 
 
      // 第二种方法 
      xmlreader reader = parser.getxmlreader(); 
      reader.setcontenthandler(this); 
      reader.parse(new inputsource(is)); 
 
    } catch (exception e) { 
      // todo: handle exception 
      e.printstacktrace(); 
    } 
 
    return persons; 
  } 

上面的代码就不解释了   只要将inputstream对象传入  就可以解析出内容
看完了代码,我来给出完整的代码

package com.example.demo.utils; 
 
import java.io.inputstream; 
import java.util.arraylist; 
import java.util.list; 
 
import javax.xml.parsers.saxparser; 
import javax.xml.parsers.saxparserfactory; 
 
import org.xml.sax.attributes; 
import org.xml.sax.inputsource; 
import org.xml.sax.saxexception; 
import org.xml.sax.xmlreader; 
import org.xml.sax.helpers.defaulthandler; 
 
import com.example.demo.person; 
 
public class sax_parserxml extends defaulthandler { 
 
  list<person> persons; 
  person person; 
  // 当前节点 
  string curnode; 
 
  public list<person> readxml(inputstream is) { 
 
    saxparserfactory factory = saxparserfactory.newinstance(); 
    try { 
      saxparser parser = factory.newsaxparser(); 
 
      // 第一种方法 
      // parser.parse(is, this); 
 
      // 第二种方法 
      xmlreader reader = parser.getxmlreader(); 
      reader.setcontenthandler(this); 
      reader.parse(new inputsource(is)); 
 
    } catch (exception e) { 
      // todo: handle exception 
      e.printstacktrace(); 
    } 
 
    return persons; 
  } 
 
  /** 
   * 当开始解析xml文件的声明的时候就会触发这个事件, 可以做一些初始化的工作 
   * */ 
  @override 
  public void startdocument() throws saxexception { 
    // todo auto-generated method stub 
    super.startdocument(); 
    // 初始化list 
    persons = new arraylist<person>(); 
  } 
 
  /** 
   * 当开始解析元素的开始标签的时候,就会触发这个事件 
   * */ 
  @override 
  public void startelement(string uri, string localname, string qname, 
      attributes attributes) throws saxexception { 
    // todo auto-generated method stub 
    super.startelement(uri, localname, qname, attributes); 
 
    // 如果读到是person标签 开始存储 
    if (localname.equals("person")) { 
      person = new person(); 
      person.set_id(attributes.getvalue("id")); 
    } 
    curnode = localname; 
  } 
 
  /** 
   * 当读到文本元素的时候要触发这个事件. 
   * */ 
  @override 
  public void characters(char[] ch, int start, int length) 
      throws saxexception { 
    // todo auto-generated method stub 
    super.characters(ch, start, length); 
 
    if (person != null) { 
      // 取出目前元素对应的值 
      string txt = new string(ch, start, length); 
      // 判断元素是否是name 
      if (curnode.equals("name")) { 
        // 将取出的值添加到person对象 
        person.set_name(txt); 
      } else if (curnode.equals("age")) { 
        person.set_age(txt); 
      } 
    } 
  } 
 
  /** 
   * 当读到结束标签的时候 就会触发这个事件 
   * */ 
  @override 
  public void endelement(string uri, string localname, string qname) 
      throws saxexception { 
    // todo auto-generated method stub 
    super.endelement(uri, localname, qname); 
 
    // 如果是person结尾 并且person不为空,添加到list 
    if (localname.equals("person") && person != null) { 
      persons.add(person); 
      person = null; 
    } 
 
    curnode = ""; 
  } 
 
} 

 写个方法调用下这个类

list<person> persons = new sax_parserxml().readxml(is); 
      stringbuffer buffer = new stringbuffer(); 
      for (int i = 0; i < persons.size(); i++) { 
        person person =persons.get(i); 
        buffer.append("id:" + person.get_id() + "  "); 
        buffer.append("name:" + person.get_name() + "  "); 
        buffer.append("age:" + person.get_age() + "\n"); 
      } 
      toast.maketext(activity, buffer, toast.length_long).show(); 

 如果你看到下面的界面  说明解析成功了~

Java中使用DOM和SAX解析XML文件的方法示例

小结:

dom(文件对象模型)解析:解析器读入整个文档,然后构建一个驻留内存的树结构,然后代码就可以根据dom接口来操作这个树结构了。 
 
  优点:整个文档读入内存,方便操作:支持修改、删除和重现排列等多种功能。 
 
  缺点:将整个文档读入内存中,保留了过多的不需要的节点,浪费内存和空间。 
 
  使用场合:一旦读入文档,还需要多次对文档进行操作,并且在硬件资源充足的情况下(内存,cpu)。 
 
为了解决dom解析存在的问题,就出现了sax解析。其特点为: 
 
  优点:不用实现调入整个文档,占用资源少。尤其在嵌入式环境中,如android,极力推荐使用sax解析。 
 
  缺点:不像dom解析一样将文档长期驻留在内存中,数据不是持久的。如果事件过后没有保存数据,数据就会丢失。 
 
  使用场合:机器有性能限制