使用SAX解析文档 --《第一行代码Android》学习笔记

原创 2015年11月19日 01:11:55

一、工作流程

SAX(Simple API for XML)使用回调模型与用户代码交互,这是一种基于事件的编程模型。基于事件的编程模型的特点在于用户代码不是主动去执行,也就是说,它从来不会去指使XML解析器去做这做那,它是被动的,在等待被调用,然后才会去执行程序。

SAX由解析器用户代码组成。在处理整个文档时,解析器负责逐个依次扫描文档元素,用户代码负责侦听事件并执行响应,当解析器遇到标签、文本等元素,都会触发相应的用户代码。在SAX的解析过程中,读取到文档开头元素开头元素内容元素结尾文档结尾分别会调用:startDocument()startElement()characters()endElement()endDocument()、开发人员可以根据需要,为感兴趣的事件编写相应的处理逻辑。

这里写图片描述

(若想知道文档元素是如何触发相应事件的,也许阅读SAX源代码会有些帮助?SAX源代码下载


二、用户代码

查看Android API可知,startDocument()startElement()characters()endElement()endDocument() 这五个方法在 ContentHandler 接口中被定义,若想使用这五个方法,首先必须实现ContentHandler接口。

然而这里我们不需要直接去定义一个类去implements ContentHandler,因为Java已经帮我们做好了这个事情,DefaultHandler 类实现了ContentHandler接口并重写了上述的五个方法,但是都是空方法,”By default, do nothing. Application writers may override this method in a subclass to take specific actions”,在默认情况下,什么都不做,开发人员可以在子类中重写方法以便作出针对性的处理。

所以第一步,我们新建一个类并继承DefaultHandler,并重写父类的五个方法。

  • 《第一行代码》书中把新建的类命名为ContentHandler,注意这里的ContentHandler为自定义的类名,不是org.xml.sax.ContentHandler,为避免混淆,建议将自定义的类名命名为SaxHandler、MyHandler等其它名字

  • 如果按照书中的写法,将新建的类命名为ContentHandler,那么在MainActivity中使用ContentHandler handler = new ContentHandler();时,不可以导入包org.xml.sax.ContentHandler!否则使用的ContentHandler就不是自定义的类了

public class MyHandler extends DefaultHandler {

    @Override
    public void startDocument() throws SAXException {
        ...
    }
    @Override
    public void startElement(String uri, String localName, String qName, Attributes attributes) {
        ...
    }
    @Override
    public void characters(char[] ch, int start, int length) throws SAXException {
        ...
    }
    @Override
    public void endElement(String uri, String localName, String qName) throws SAXException {
        ...
    }
    @Override
    public void endDocument() throws SAXException {
    }
}


三、解析器

有以下两种方法实现解析器:

1. 使用XMLReader(《第一行代码》书中方法)

SAXParserFactory factory = SAXParserFactory.newInstance();  //新建工厂类
XMLReader xmlReader = factory.newSAXParser().getXMLReader(); //通过工厂类获取SAXParser类实例,并再次获取到XMLReader实例
MyHandler myHandler = new MyHandler();
xmlReader.setContentHandler(myHandler);
xmlReader.parse(new InputSource(new StringReader(response)));

我们新建一个工厂类SAXParserFactory,让工厂类产生一个SAX的解析类SAXParser并从SAXPsrser中得到一个XMLReader实例。综上所述,SAX解析XML可以归纳为六个步骤:

  1. 新建一个工厂类SAXParserFactory
    SAXParserFactory factory =SAXParserFactory.newInstance();

  2. 让工厂类产生一个SAX的解析类SAXParser
    SAXParser parser = factory.newSAXParser();

  3. 从SAXParser中得到一个XMLReader实例
    XMLReader reader = parser.getXMLReader();

  4. 得到内容处理器
    MyHandler myHandler = new MyHandler();

  5. 把自己写的handler注册到XMLReader中
    reader.setContentHandler(myHandler);

  6. 将一个xml变成一个java可以处理的InputStream流后,解析开始
    reader.parse(newInputSource(new FileInputStream("books.xml")));
    或者把xml变成字符串,
    reader.parse(new InputSource(new StringReader(xmlString)));

2. 不使用XMLReader,直接使用SAXParser

上述的步骤1、2不变,产生SAX的解析类SAXParser后,不必获取到XMLReader类实例,查看API 可以看到SAXParser类下也有parse方法,直接使用parse方法解析,同时传入两个参数——要解析的数据和用户代码(回调的方法)

SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser parser = factory.newSAXParser();

MyHandler myHandler = new MyHandler();
parser.parse(new InputSource(new StringReader(response)), myHandler);

以上这两种方法解析所得的结果完全一致,任选一种方法即可,似乎第一种方法使用更广泛…至于为什么要对同样的目的设计两种方法,我不清楚,但是注意到XMLReader包含在由第三方提供的包org.xml.sax.XMLReader中。

3. 使用XMLReaderFactory获取XMLReader实例(注意要设置系统属性!)

在 1 中通过工厂类SAXParserFactory获取到SAXParser再获取到XMLReader,然而还可以通过另外的工厂类XMLReaderFactory,使用静态方法createXMLReader获取。XMLReader myReader = XMLReaderFactory.createXMLReader();

然而这种方法可以通过编译,但是运行时会提示错误“Can’t create default XMLReader; is system property org.xml.sax.driver set?”。解决办法:在MainActivity类的onCreate方法中添加语句设置driver系统属性,System.setProperty("org.xml.sax.driver","org.xmlpull.v1.sax2.Driver");,通过这个语句将org.xml.sax.driver的值设定为org.xmlpull.v1.sax2.Driver即可。

XMLReader xmlReader = XMLReaderFactory.createXMLReader();

MyHandler myHandler = new MyHandler();
xmlReader.setContentHandler(myHandler);
xmlReader.parse(new InputSource(new StringReader(response)));

这样做可能的原因是:所有兼容SAX标准的XML解析器都必须实现SAX中的org.xml.sax.XMLReader接口,不同解析器提供商实现了接口的类的名字(或者包名)不同。那么实例化XMLReader时,利用不同提供商的类实例化所使用的语句也就不同。为了避免这种情况,才引入了XMLReaderFactory类,通过XMLReaderFactory.createXMLReader();方法统一了实例化的语句。但即使XMLReaderFactory类,也必须指定解析器提供商,通过设置org.xml.sax.driver属性来指定。

在写Java程序时,这个系统属性可以在运行时使用java -Dorg.xml.sax.driver=org.apache.xerces.parsers.SAXparser来指定。


四、待解决问题

解析器读取标签之后还会读取一个空白本文,即使看上去那里并没有任何内容?


参考书籍:第一行代码Android、Java与XML(第三版)Oreilly&中国电力出版社

版权声明:欢迎转载,转载请注明出处。

相关文章推荐

解析xml出现Can't create default XMLReader; is system property org.xml.sax.driver set?错误

最近闲着没事所以就在捣鼓微信订阅号

第一行代码总结:10网络:10.2.2使用HttpClient

HttpClient是Apache提供的HTTP网络访问接口。因为是一个接口,因此无法创建它的实例,通常情况下都会创建一个DefaultHttpClient的实例。 步骤: 1、获得HttpCli...

第一行代码-10.4 解析JSON格式数据

和XML相比,JSON的优势在于体积小,网络上传输的时候更省流量,但是缺点在于语义性较差,看起来不如XML直观。   准备工作:创建get_data.json,然后输入内容:[{"id":"5","...

第一行代码总结:10网络:10.3解析XML格式数据:

在网络上传输的数据是格式化后的数据,这种数据会有一定的结构规格和语义,当另一方收到数据消息之后就可以按照相同的结构规格进行解析,从而取出他想要的那部分内容。 最常用的格式有两种:XML和JSON. ...

第一行代码-10.3 解析XML数据格式

准备工作:首先开启wamp server,然后在wamp/www目录下新建get_data.xml文件,并写入以下内容: 1 Google Maps 1.0 2 Chrome 2.1 </

第一行代码总结:10网络:10.4解析JSON格式数据10.4.1使用JSONObject

10.4解析JSON格式数据 比起XML,JSON的主要优势在于:体积更小,在网络上传输的时候更省流量。                 ...

第一行代码 第九章 网络技术 - 解析xml数据

在网络上传输数据时,最常用的格式有两种:XML和JSON.解析XML格式的数据也有很多方式,比较常用的两种是:Pull解析和SAX解析。一、Pull解析方式 实例: MainActivity.ja...

第一行代码 第九章 网络技术 - 解析JSON格式数据

比起XML格式数据,解析JSON格式数据会更省流量。 解析的方式也有很多种方法,可以使用官方提供的JSONObject,也可以使用谷歌的开源库GSON。一、JSONObject 实例: Main...

Android定制ListView的界面(使用继承自ArrayAdapter的自定义适配器)--《第一行代码Android》学习笔记

ListView控件用于实现程序中的内容在屏幕上滚动的效果,大部分日常使用的应用程序都会使用ListView控件。一、简单ListView的用法简单ListView,即实现只有多个文本内容的滚动显示(...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)