Java基础之SAX解析XML

1. Java解析XML简介

Java库中提供了两种XML解析器:

  • 像文档对象模型(Document Object Model,DOM)解析器这的树型解析器(tree parse),它们将读入的XML文档转换成树结构。
  • 像XML简单API(Simple API for XML,SAX)解析器这样的流机制解析器(streaming parser),它们在读入XML文档时生成相应的事件。

2. SAX简介

SAX解析器在解析XML输入数据的各个组成部分时会报告事件,但不会以任何方式存储文档,而是由事件处理器建立相应的数据结构。实际上DOM解析器是在SAX解析器的基础上构建的,它在接收到解析器事件时构建DOM树。

3. 使用SAX解析器

SAX解析器架构图

在使用SAX解析器时,需要一个处理器来为各种解析器事件定义事件动作。DefaultHandler接口定义了若干个在解析文档时解析器会调用的回调方法。下面是最重要的几个方法:

  • startElement和endElement在每当遇到起始或终止标签时调用。
  • characters在每当遇到字符数据时调用。
  • startDocument和endDocument分别在文档开始和结束时各调用一次。

例如,在解析以下片段时:

<person>
    <name type="string">韩信</name>
    <age>25</age>
</person>

解析器会产生以下回调:
1)startElement,元素名:person
2)startElement,元素名:name ,属性:type="string"
3)characters,内容:韩信
4)endElement,元素名:name
5)startElement,元素名:age
6)characters,内容:25
7)endElement,元素名:age
8)endElement,元素名:person

处理器必须覆盖这些方法,让它们执行在解析文件时我们想让它们执行的动作。

下面通过一个简单的demo体会SAX解析XML的过程。

3.1 准备xml

首先准备person.xml,内容如下

<?xml version="1.0" encoding="UTF-8" ?>
<persons>
    <person>
      <name>韩信</name>
      <age>25</age>
   </person>
   <person>
      <name>李白</name>
      <age>23</age>
   </person>
</persons>

3.2 解析代码

  • 获取解析工厂
  • 从解析工厂获取解析器
  • 得到解读器
  • 设置内容处理器
  • 读取xml的文档内容
package cn.lastwhisper.javabasic.Sax;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.io.IOException;

/**
 * @author lastwhisper
 * @desc SAX方式解析xml文件
 * @email gaojun56@163.com
 */
public class SaxTest {
    public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException {
        // SAX解析
        // 1.获取解析工厂
        SAXParserFactory factory = SAXParserFactory.newInstance();
        // 2.从解析工厂获取解析器
        SAXParser parse = factory.newSAXParser();
        // 3.得到解读器
        XMLReader reader=parse.getXMLReader();
        // 4.设置内容处理器
        reader.setContentHandler(new PHandler());
        // 5.读取xml的文档内容
        reader.parse("src/main/java/cn/lastwhisper/javabasic/Sax/person.xml");

    }

}

class PHandler extends DefaultHandler {
    /**
     * @author lastwhisper
     * @desc 文档解析开始时调用,该方法只会调用一次
     * @param
     * @return void
     */
    @Override
    public void startDocument() throws SAXException {
        System.out.println("----解析文档开始----");
    }

    /**
     * @author lastwhisper
     * @desc 每当遇到起始标签时调用
     * @param uri xml文档的命名空间
     * @param localName 标签的名字
     * @param qName 带命名空间的标签的名字
     * @param attributes 标签的属性集
     * @return void
     */
    @Override
    public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
        System.out.println("标签<"+qName + ">解析开始");
    }

    /**
     * @author lastwhisper
     * @desc 解析标签内的内容的时候调用
     * @param ch 当前读取到的TextNode(文本节点)的字节数组
     * @param start 字节开始的位置,为0则读取全部
     * @param length 当前TextNode的长度
     * @return void
     */
    @Override
    public void characters(char[] ch, int start, int length) throws SAXException {
        String contents = new String(ch, start, length).trim();
        if (contents.length() > 0) {
            System.out.println("内容为-->" + contents);
        } else {
            System.out.println("内容为-->" + "空");
        }
    }
    /**
     * @author lastwhisper
     * @desc 每当遇到结束标签时调用
     * @param uri xml文档的命名空间
     * @param localName 标签的名字
     * @param qName 带命名空间的标签的名字
     * @return void
     */
    @Override
    public void endElement(String uri, String localName, String qName) throws SAXException {
        System.out.println("标签</"+qName + ">解析结束");
    }
    /**
     * @author lastwhisper
     * @desc 文档解析结束后调用,该方法只会调用一次
     * @param
     * @return void
     */
    @Override
    public void endDocument() throws SAXException {
        System.out.println("----解析文档结束----");
    }
}

运行main函数,查看运行结果

运行结果

3.3 分析解析过程

----解析文档开始---- 调用startDocument
标签<persons>解析开始 调用startElement
内容为-->空 调用characters,因为<persons></persons>之间只有标签没有内容,所以为空。
标签<person>解析开始 调用startElement
内容为-->空 调用characters,因为<person></person>之间只有标签没有内容,所以为空。
标签<name>解析开始 调用startElement
内容为-->韩信 调用characters,<name>韩信</name>之间内容为:韩信
标签</name>解析结束 调用endElement
内容为-->空 调用characters,每次执行完调用endElement之后会调用一次characters
标签<age>解析开始 调用startElement
内容为-->25 调用characters,<age>25</age>之间内容为:25
标签</age>解析结束 调用endElement
内容为-->空 调用characters,每次执行完调用endElement之后会调用一次characters
标签</person>解析结束 调用endElement
内容为-->空 调用characters,每次执行完调用endElement之后会调用一次characters
...
标签<person>解析开始
内容为-->空
标签<name>解析开始
内容为-->李白
标签</name>解析结束
内容为-->空
标签<age>解析开始
内容为-->23
标签</age>解析结束
内容为-->空
标签</person>解析结束
内容为-->空
标签</persons>解析结束
----解析文档结束---- 调用endDocument,xml解析结束

4. 解析xml到pojo对象

待解析的xml,person.xml

<?xml version="1.0" encoding="UTF-8" ?>
<persons>
    <person>
      <name>韩信</name>
      <age>25</age>
   </person>
   <person>
      <name>李白</name>
      <age>23</age>
   </person>
</persons>

xml转换为pojo的代码

package cn.lastwhisper.javabasic.Sax;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * @author lastwhisper
 * @desc 将xml数据解析到pojo中
 *
 * @email gaojun56@163.com
 */
public class SaxXmlToPojo {
    public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException {
        // SAX解析
        // 1.获取解析工厂
        SAXParserFactory factory = SAXParserFactory.newInstance();
        // 2.从解析工厂获取解析器
        SAXParser parse = factory.newSAXParser();
        // 3.得到解读器
        XMLReader reader = parse.getXMLReader();
        // 4.设置内容处理器
        PersonHandler personHandler = new PersonHandler();
        reader.setContentHandler(personHandler);
        // 5.读取xml的文档内容
        reader.parse("src/main/java/cn/lastwhisper/javabasic/Sax/person.xml");

        List<Person> persons = personHandler.getPersons();
        for (Person person : persons) {
            System.out.println("姓名:" + person.getName() + " 年龄:" + person.getAge());
        }
    }

}

class PersonHandler extends DefaultHandler {
    private List<Person> persons;
    private Person person;
    private String tag; // 存储操作标签

    /**
     * @author lastwhisper
     * @desc 文档解析开始时调用,该方法只会调用一次
     * @param
     * @return void
     */
    @Override
    public void startDocument() throws SAXException {
        persons = new ArrayList<Person>();
    }

    /**
     * @author lastwhisper
     * @desc 标签(节点)解析开始时调用
     * @param uri xml文档的命名空间
     * @param localName 标签的名字
     * @param qName 带命名空间的标签的名字
     * @param attributes 标签的属性集
     * @return void
     */
    @Override
    public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
        tag = qName;
        if ("person".equals(tag)) {
            person = new Person();
        }
    }

    /**
     * @author lastwhisper
     * @desc 解析标签的内容的时候调用
     * @param ch  字符
     * @param start 字符数组中的起始位置
     * @param length 要从字符数组使用的字符数
     * @return void
     */
    @Override
    public void characters(char[] ch, int start, int length) throws SAXException {
        String contents = new String(ch, start, length).trim();
        if ("name".equals(tag)) {
            person.setName(contents);
        } else if ("age".equals(tag)) {
            if (contents.length() > 0) {
                person.setAge(Integer.valueOf(contents));
            }
        }
    }

    /**
     * @author lastwhisper
     * @desc 标签(节点)解析结束后调用
     * @param uri xml文档的命名空间
     * @param localName 标签的名字
     * @param qName 带命名空间的标签的名字
     * @return void
     */
    @Override
    public void endElement(String uri, String localName, String qName) throws SAXException {
        if ("person".equals(qName)) {
            persons.add(person);
        }
        tag = null; //tag丢弃了
    }

    /**
     * @author lastwhisper
     * @desc 文档解析结束后调用,该方法只会调用一次
     * @param
     * @return void
     */
    @Override
    public void endDocument() throws SAXException {
    }

    public List<Person> getPersons() {
        return persons;
    }
}

运行结果:

运行结果
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 230,825评论 6 546
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 99,814评论 3 429
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 178,980评论 0 384
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 64,064评论 1 319
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 72,779评论 6 414
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 56,109评论 1 330
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 44,099评论 3 450
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 43,287评论 0 291
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 49,799评论 1 338
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 41,515评论 3 361
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 43,750评论 1 375
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 39,221评论 5 365
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 44,933评论 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 35,327评论 0 28
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 36,667评论 1 296
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 52,492评论 3 400
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 48,703评论 2 380

推荐阅读更多精彩内容

  • 1 XML解析No29 【 XML:可拓展标记语言,语言和HTML类似,也是一种标记语言。 特点:标记是自定义...
    征程_Journey阅读 1,670评论 0 9
  • 1. XML总结 1.1. XML简介 XML : 可扩展的标记语言。(和HTML非常类似的) 可扩展的。 自定义...
    Ethan_Walker阅读 3,095评论 0 12
  • 什么是XML? XML 指可扩展标记语言(eXtensible Markup Language)。 你可以通过本站...
    Canon_2020阅读 799评论 0 0
  • 一、绪论 上周工作需要了解项目的一些大体内容,结果在xml解析这一块看的迷迷糊糊的,所以在这里把学习到xm...
    cao健强阅读 4,080评论 1 7
  • 一. Java基础部分.................................................
    wy_sure阅读 3,832评论 0 11