SAX 的解析步骤:
(1)写一个类继承 DefaultHandler, 实现自己的事件处理方法
(2)在主程序中建立 SAXParserFactory
(3)可以设置这个factory 的参数
(4)从这个factory 得到SAXParser
(5)解析XML文件
获取输入源
InputSource is = new InputSource(InputStream byteStream);
资源来源于文件:InputSource is = new InputSource(getResources().openRawResource(R.raw.xmltwitter));
资源来源于字符串String:String str = "";//add your string content InputStream inputStream = new ByteArrayInputStream(str.getBytes());
资源来源于网络(如RSS订阅,WebService等):// setup the url String urlString = "http://feed.xxx.com"; java.net.URL url = new URL(urlString); InputSource is = new InputSource(url.openStream());
解析流程首先需要根据已知的XML文件的约定,创建一个继承DefaultHandler 的子类。最后,再说说两点,一个就是startElement(String uri, String localName, String qName, Attributes attributes) 方法中各参数的简单意义。
注:SAX 解析器是一个基于事件的解析器,这就意味着使用 SAX 进行解析时要建立真实文件。
在文档开始和结束、标记开始和结束、发现数据时,事件被触发。这意味着您必须定义一个数据结构来保留感兴趣的数据、抛弃余下的。所以实际上的解析工作,是在这个Handler 里进行的。
重写以下这几个父类的方法//通知文档开始,如需初始化,可在此方法里面处理 public void startDocument() throws SAXException; //通知文档结束 public void endDocument() throws SAXException; //文档节点开始 public void startElement(String namespaceURI, String localName, String qName,Attributes atts) throws SAXException; //文档节点结束 public void endElement(String namespaceURI, String localName, String qName) throws SAXException; public void characters(char ch[], int start, int length);
现在先解析下面这个简单的XML文件xmlFile.xml,看看这几个方法是什么时候调用,用在什么地方。
<root><!--注意这里有个换行符--> </root>
上面5个需要重写的方法仅仅做一件事:输出该方法的方法名:
int i = 1; public void startDocument() throws SAXException{ System.out.println(i++); System.out.println(".startDocument()"); } public void endDocument() throws SAXException{ System.out.println(i++); System.out.println(".endDocument()"); } public void startElement(String namespaceURI, String localName, String qName,Attributes atts) throws SAXException{ System.out.println(i++); System.out.println(".startElement()"); } public void endElement(String namespaceURI, String localName, String qName) throws SAXException{ System.out.println(i++"); System.out.println(".endElement()"); } public void characters(char ch[], int start, int length){ System.out.println(i++); System.out.println(".characters()"); }
具体运行代码如下 SAXDemo.java
import org.xml.sax.helpers.DefaultHandler; import javax.xml.parsers.*; import org.xml.sax.*; public class SAXDemo extends DefaultHandler{ private int i = 1;//记录执行序号 public void startDocument(){ System.out.print(i);i++; System.out.println(".startDocument"); }; public void startElement(String uri,String localName, String qName,Attributes attributes){ System.out.print(i++); System.out.println(".startElement"); } public void characters(char[] ch,int start, int length){ System.out.print(i++); System.out.println(".characters"); } public void endElement(String uri,String localName,String qName){ System.out.print(i);i++; System.out.println(".endElement"); } public void endDocument(){ System.out.print(i);i++; System.out.println(".endDocument"); } //解析xmlFile.xml文件 public static void main( String[] args ){ try{ DefaultHandler dh = new MySaxParser(); SAXParserFactory spf = SAXParserFactory.newInstance(); SAXParser sp = spf.newSAXParser(); sp.parse( "xmlFile.xml", dh ); }catch( Exception e ){ e.printStackTrace(); } } }
运行后,你会看到如下面的结果
1.startDocument()
2.startElement()
3.characters()
4.endElement()
5.endDocument()
再解析如下的XML
<root></root>
控制台会输出:
1.startDocument()
2.startElement()
3.endElement()
4.endDocument()
以及
<root> <sub>SUB</sub> </root>
控制台输出
1.startDocument()
2.startElement()
3.characters()
4.startElement()
5.characters()
6.endElement()
7.characters()
8.endElement()
9.endDocument()
以及
<root><sub>SUB</sub><root>
控制台输出
1.startDocument()
2.startElement()
3.startElement()
4.characters()
5.endElement()
6.endElement()
7.endDocument()
目前可以得出以下结论:
1.每个Document开始,都会调用startDocument(),仅调用一次。
2.对于每一个节点 ,都会调用startElement(),如果不是叶子节点(即最底层的节点),还会继续调用startElement()。
如上面,顺序是: startElement()[解析<root>] --- startElemtnt() [解析<sub>] --- endElement()[解析</sub>] --- endElement()[解析</root>]3.在节点与节点之间,如有任意字符(包括回车,换行符,空格等),都会执行characters()进行检查,无论是
开始节点 与 结束节点 之间(如<sub> & </sub>),
开始节点 与 开始节点 之间(如<root> & <sub>),
结束节点 与 开始节点 之间(如</sub> & <sub2>,假设同级有<sub2>,可自行测试),
结束节点 与 结束节点 之间(如</sub> & </root>).4.访问节点的算法属于深度遍历,有点像二叉树前序算法遍历访问。(这个纯粹个人观点,对实际的解析算法理解比较浅显)
清楚了整个解析的流程,那么什么时候从xml取得对应元素的值,赋值到对应的 结果输出字符串,或者 对应的 JavaBean,就由程序员使用一个布尔值来控制了。
URI:命名空间的URI,
localName:不带命名空间前缀的标签名,通常获得标签字串的就是用这个
qName:带命名空间前缀的标签名
attributes:可以得到所有的属性名和相应的值。
//常用使用下面的方法取得是属性名和相应的值 String value = org.xml.sax.Attributes.getValue(int index);// 获得 相应的值 String name = org.xml.sax.Attributes.getQName(int index);// 获得 属性名
另外要说的就是上面提到“SAX 解析步骤”的实现代码:// create the factory 创建工厂类 SAXParserFactory factory = SAXParserFactory.newInstance(); // create a parser 创建解析器 SAXParser parser = factory.newSAXParser(); // create the reader (scanner) 创建Reader类 XMLReader xmlreader = parser.getXMLReader();
其实,最后解析的方法还可以用以下几个方法SAXParser.parse();
public void parse(String uri, DefaultHandler dh) throws SAXException, IOException ;//上面的SAXDemo.java就是使用这一个方法 public void parse(File f, DefaultHandler dh) throws SAXException, IOException; public void parse(InputSource is, DefaultHandler dh) throws SAXException, IOException; public void parse(InputStream is, DefaultHandler dh) throws SAXException, IOException;
本来想在这文章里面提供一个示例代码,再写到最后的时候,找到一篇很好的分析SAX原理文章,里面也附上了相关的例子,
《使用SAX读取XML文件》http://justsee.iteye.com/blog/923439
我相信,看完之后会对SAX的使用有更深刻的理解。