java的xml学习[使用SAX解析XML文件]

 DOM:文档驱动DOM在解析文件之前把整个文档装入内存,处理大型文件时其性能很差,是由于DOM的树结构所造成的,此结构占用的内存较多

    SAX(simple API for XML)采用事件驱动的方式解析文档。简单点说,如同在电影院看电影一样,从头到尾看一遍就完了,不能回退(DOM可来来回回读取)在看电影的过程中,每遇到一个情节,一段泪水,一次擦肩,你都会调动大脑和神经去接收或处理这些信息。同样,在SAX的解析过程中,读取到文档开头、结尾,元素的开头和结尾都会触发一些回调方法,你可以在这些回调方法中进行相应事件处理。这四个方法是:startDocument() 、 endDocument()、 startElement()、 endElement()

    此外,光读取到节点处是不够的,我们还需要characters()方法来仔细处理元素内包含的内容。将这些回调方法集合起来,便形成了一个类,这个类也就是我们需要的触发器(继承自DefaultHandler,并重写其中的5个方法即可)。一般从main()方法中读取文档,却在触发器中处理文档,这就是所谓的事件驱动解析方法。

https://i-blog.csdnimg.cn/blog_migrate/51a14b8d107d67aaa465aa59df1e6ea5.jpeg

    如上图,在触发器中,首先开始读取文档(startDocument),然后开始逐个解析元素(startElement),每个元素中的内容会回调characters()方法,接着结束元素读取(endElement),所有元素读取完后,结束文档解析(EndDocument)。

    

SAX方式解析XML文件的步骤:

    1. 通过SAXParserFactory的静态方法newInstance()方法获取SAXParserFactory的实例factory;

    2. 通过SAXParserFactory实例的newSAXParser()方法返回一个SAXParser实例parser;

    3. 创建一个类继承自DefaultHandler,重写其中的一些方法进行业务处理并创建这个类的实例handler;

    4. 通过SAXParser对象的parse(String uri,DefaultHandler dh)方法解析xml文件。 


-    解析开始:读到XML的声明;

<?xml version = "1.0" encoding = "UTF-8"?>
-    解析结束:读到最后一个结束标记。
</bookstore>
实现如下:
import java.io.IOException;
 
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
 
import org.xml.sax.SAXException;
 
public class SAXTest {
 
    public static void main(String[] args) {
    // 1.获取一个SAXParserFactory的实例
    SAXParserFactory factory = SAXParserFactory.newInstance();
    try {
        // 2.通过factory获取SAXParser实例
        SAXParser parser = factory.newSAXParser();
        // 3.创建一个SAXParserHandler对象
        SAXParserHandler handler = new SAXParserHandler();
        // 4.通过SAXParser对象的parse(String uri,DefaultHandler dh)方法解析xml文件
        parser.parse("books.xml", handler);
    } catch (ParserConfigurationException e) {
        e.printStackTrace();
    } catch (SAXException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
    }
 
}

SAXParserHandler类,继承自DefaultHandler并重写其中的一些业务方法:

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
 
/**
 * 创建一个类继承自DefaultHandler,重写其中的一些方法用来进行业务处理
 *
 */
public class SAXParserHandler extends DefaultHandler {
    int bookIndex = 0;// 定义一个全局变量用于保存当前遍历的书的索引(当前正在遍历第几本书)
    private String value = null;//保存节点中的内容
 
    /**
     * 用来标志解析开始
     */
    @Override
    public void startDocument() throws SAXException {
        super.startDocument();
        System.out.println("=============== SAX解析开始 ===============\n");
    }
 
    /**
     * 用来标志解析结束
     */
    @Override
    public void endDocument() throws SAXException {
        super.endDocument();
        System.out.println("=============== SAX解析结束 ===============");
    }
 
    /**
     * 解析XML文件的开始标签,uri-名称空间,localName-名称空间的标签,如果没有名称空间,则为空
     * qName-标签名,attributes-属性集合
     */
    @Override
    public void startElement(String uri, String localName, String qName,
        Attributes attributes) throws SAXException {
        super.startElement(uri, localName, qName, attributes);
        System.out.println(">>>>>>>>>>>开始解析元素:" + qName);
        // 仅仅book节点有属性
        if ("book".equals(qName)) {
            bookIndex++;
            System.out.println("***********开始遍历第" + (bookIndex)
                + "本书***********");
            int numberOfAttributes = attributes.getLength();// 获取属性个数
            for (int i = 0; i < numberOfAttributes; i++) {
                System.out.print("book元素的第" + (i + 1) + "个属性名:"
                    + attributes.getQName(i));
                System.out.println(",属性值:" + attributes.getValue(i));
            }
        } else if (!"bookstore".equals(qName)) {
            System.out.print("节点名:" + qName);
        }
    }
 
    /**
     * 字符数组ch-元素中的内容,start-开始位置,length-长度
     */
    @Override
    public void characters(char[] ch, int start, int length)
        throws SAXException {
        super.characters(ch, start, length);
        value = new String(ch, start, length);
        if (!"".equals(value.trim())) {// 过滤空白文本(和DOM类似)
            System.out.println("-节点值:" + value);
        }
    }
 
    /**
     * 解析XML文件的结束标签
     */
    @Override
    public void endElement(String uri, String localName, String qName)
        throws SAXException {
        super.endElement(uri, localName, qName);
        System.out.println("<<<<<<<<<<<结束解析" + qName);
        // 判断是否一本书遍历结束(handler指向</book>)
        if ("book".equals(qName)) {
            System.out.println("***********结束遍历第" + (bookIndex)
                + "本书***********\n");
        } 
    }
}

运行结果:

https://i-blog.csdnimg.cn/blog_migrate/7d9ecde715762cb162c11f84c5a70163.jpeg

如果已知元素的属性名,我们就可以根据属性名获得属性值(而不需要得到attributes的length然后循环遍历属性):

String attrValue = attributes.getValue("id");
System.out.println("book的属性值是:"+attrValue);

注意:DefaultHandler 中的characters(char[] ch, int start, int length)方法的3个参数:

http://img.mukewang.com/54d9b96c0001ffa806460414.jpg

    以上的代码虽然完成了XML文件的基本解析,但是不能轻易看出XML文件的结构,如果能将XML文件中的结构还原那就太好了!我们需要重写SAXParserHandler类:

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
 
/**
 * 创建一个类继承自DefaultHandler,重写其中的一些方法用来进行业务处理
 *
 */
public class SAXParserHandler extends DefaultHandler {
 
    /**
     * 用来标志解析开始
     */
    @Override
    public void startDocument() throws SAXException {
        super.startDocument();
        System.out.println("=============== SAX解析开始 ===============");
        System.out.println("<?xml version = \"1.0\" encoding = \"utf-8\" ?>");
    }
 
    /**
     * 用来标志解析结束
     */
    @Override
    public void endDocument() throws SAXException {
        super.endDocument();
        System.out.println("=============== SAX解析结束 ===============");
    }
 
    /**
     * 解析XML文件的开始标签,uri-名称空间,localName-名称空间的标签,如果没有名称空间,则为空
     * qName-标签名,attributes-属性集合
     */
    @Override
    public void startElement(String uri, String localName, String qName,
        Attributes attributes) throws SAXException {
        super.startElement(uri, localName, qName, attributes);
        System.out.print("<");
        System.out.print(qName);// 输出开始标记
        if (attributes != null) {
            // 循环输出属性
            for (int i = 0; i < attributes.getLength(); i++) {
            System.out.print(" " + attributes.getQName(i) + " = \""
                + attributes.getValue(i) + "\"");
            }
        }
        System.out.print(">");
    }
 
    /**
     * 字符数组ch-元素中的内容,start-开始位置,length-长度
     */
    @Override
    public void characters(char[] ch, int start, int length)
        throws SAXException {
        super.characters(ch, start, length);
        System.out.print(new String(ch, start, length));// 输出元素中的内容
    }
 
    /**
     * 解析XML文件的结束标签
     */
    @Override
    public void endElement(String uri, String localName, String qName)
        throws SAXException {
        super.endElement(uri, localName, qName);
        System.out.print("</");
        System.out.print(qName);// 输出结束标记
        System.out.print(">");
    }
}

 运行结果:

http://img.mukewang.com/54d9be740001e1a305600377.jpg

将XML中的数据保存到Java的对象(SAX解析)

将XML文档中的数据保存到java对象一般需要以下的几个步骤:

  1. 创建类(例如Book类),类中的属性就是XML文档中的属性名就是XML文档中的属性名和标签名,封装属性,并提供一系列的getter和setter;

  2. startElement方法中每遇到相应的开始标记就创建一个响应的对象,并为其设置属性;将characters方法中元素的内容用一个全局变量保存;在endElement方法中为对象的每一个属性设置响应的值。


实现如下:

    Book类:

public class Book {
    private String id;
    private String name;
    private String author;
    private String year;
    private String price;
    private String language;
 
    public String getId() {
    return id;
    }
 
    public void setId(String id) {
    this.id = id;
    }
 
    public String getName() {
    return name;
    }
 
    public void setName(String name) {
    this.name = name;
    }
 
    public String getAuthor() {
    return author;
    }
 
    public void setAuthor(String author) {
    this.author = author;
    }
 
    public String getYear() {
    return year;
    }
 
    public void setYear(String year) {
    this.year = year;
    }
 
    public String getPrice() {
    return price;
    }
 
    public void setPrice(String price) {
    this.price = price;
    }
 
    public String getLanguage() {
    return language;
    }
 
    public void setLanguage(String language) {
    this.language = language;
    }
 
    @Override
    public String toString() {
    return "Book [id=" + id + ", name=" + name + ", author=" + author
        + ", year=" + year + ", price=" + price + ", language="
        + language + "]";
    }
 
}
重写的SAXParserHandler类:
import java.util.ArrayList;
 
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
 
/**
 * 创建一个类继承自DefaultHandler,重写其中的一些方法用来进行业务处理
 *
 */
public class SAXParserHandler extends DefaultHandler {
    private String value = null;// 全局变量保存节点中的内容
    private Book book = null;
    private ArrayList<Book> bookList = new ArrayList<Book>();
 
    public ArrayList<Book> getBookList() {
    return bookList;
    }
 
    /**
     * 解析XML文件的开始标签,uri-名称空间,localName-名称空间的标签,如果没有名称空间,则为空
     * qName-标签名,attributes-属性集合
     */
    @Override
    public void startElement(String uri, String localName, String qName,
        Attributes attributes) throws SAXException {
    super.startElement(uri, localName, qName, attributes);
    if ("book".equals(qName)) {
        book = new Book();// 每遇到book的一个开始标记就创建一个Book对象
        String idAttr = attributes.getValue("id");
        book.setId(idAttr);// 为book对象设置id属性
    }
    }
 
    /**
     * 字符数组ch-元素中的内容,start-开始位置,length-长度
     */
    @Override
    public void characters(char[] ch, int start, int length)
        throws SAXException {
    super.characters(ch, start, length);
    value = new String(ch, start, length);
    }
 
    /**
     * 解析XML文件的结束标签
     */
    @Override
    public void endElement(String uri, String localName, String qName)
        throws SAXException {
    super.endElement(uri, localName, qName);
    if ("book".equals(qName)) {
        bookList.add(book);// book中的属性被设置新的值之前将其加入ArrayList中
    } else if ("name".equals(qName)) {// 遇到结束标记表明元素节点中的内容已经全部取得
        book.setName(value);
    } else if ("author".equals(qName)) {
        book.setAuthor(value);
    } else if ("year".equals(qName)) {
        book.setYear(value);
    } else if ("price".equals(qName)) {
        book.setPrice(value);
    } else if ("language".equals(qName)) {
        book.setLanguage(value);
    }
    }
SAXTest类:

import java.io.IOException;
import java.util.ArrayList;
 
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
 
import org.xml.sax.SAXException;
 
public class SAXTest {
 
    public static void main(String[] args) {
    // 1.获取一个SAXParserFactory的实例
    SAXParserFactory factory = SAXParserFactory.newInstance();
    try {
        // 2.通过factory获取SAXParser实例
        SAXParser parser = factory.newSAXParser();
        // 3.创建一个SAXParserHandler对象
        SAXParserHandler handler = new SAXParserHandler();
        // 4.通过SAXParser对象的parse(String uri,DefaultHandler dh)方法解析xml文件
        parser.parse("books.xml", handler);
 
        /* 将XML文件中的数据保存在Book对象中并将对象添加到ArrayList中,遍历ArrayList */
        ArrayList<Book> books = handler.getBookList();
        System.out.println("ArrayList中一共保存了" + books.size() + "本书!");
        for (Book book : books) {
        System.out.println(book);
        }
    } catch (ParserConfigurationException e) {
        e.printStackTrace();
    } catch (SAXException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
    }
 
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值