Android XML SAX 数据解析
SAX 是一个解析速度快而且占用内存少的 XML 解析器,非常适合用于 Android 等资源紧张的移动设备
SAX 解析 XML 文件采用的是事件驱动, 也就是不需要先解析整个文档,而是在解析文档过程中,判断读到的字符是否匹配 XML 语法的某一部分 (文档开头,文档结束,标签开头,变迁结束等),如果匹配的话,就会触发相应的事件,这些事件都定义在 ContentHandler
接口中
ContentHandler
ContentHandler
默认需要实现类重写以下几个方法
方法 | 说明 |
---|---|
void characters(char[] ch, int start, int length) | 读到内容时回调该方法 ch 用于存放该文档的内容 start / end 是当前内容在文档内容中的起始和结束位置 |
void endDocument() | 读到文档结束标志时触发,在这里可以完成一些收尾工作 |
void endElement(String uri, String localName, String qName) | 遇到结束标签的时候调用该方法 |
void endPrefixMapping(String prefix) | 结束命名空间映射的范围时触发 |
void ignorableWhitespace(char[] ch, int start, int length) | 读到可以忽略的空白时会回调该方法 |
void processingInstruction(String target, String data) | 读到处理指令时回调 |
void setDocumentLocator(Locator locator) | 接收一个对象以查找 SAX 文档事件的来源 |
void skippedEntity(String name) | 遇到可跳过 Entity 时触发 |
void startDocument() | 读到文档开始标志时触发,在这里可以完成一些初始化工作 |
void startElement(String uri, String localName, String qName, Attributes atts) | 遇到标签开始时回调,参数依次为命名空间,带命名控件的标签名,不带命名空间的标签名,所有属性 |
void startPrefixMapping(String prefix, String uri) | 遇到命名空间映射的范围时触发 |
跟其它 Android 中的接口一样,如果自己实现 ContentHandler
就有一大堆的方法需要重写,还好, Android 了解这一点,已经帮我们默认实现了一个 DefaultHandler
,我们只需要继承它然后重写需要的方法即可
注意: 节点之间的空白文本也是文本节点,解析的时候也会走这些节点
使用 SAX 解析 XML 的一般流程
-
获取文件资源建立输入流对象
InputStream is = getAssets().open("languages.xml");
-
自定义个一个
ContentHandler
继承DefaultHandler
重写需要的方法public class SaxHelper extends DefaultHandler{}
-
创建 XML 解析处理器
SaxHelper ss = new SaxHelper();
-
创建一个 SAX 解析器工厂实例
SAXParserFactory factory = SAXParserFactory.newInstance();
-
通过 SAX 解析起工厂创建一个 SAX 解析器对象
SAXParser parser = factory.newSAXParser();
-
将 xml 解析处理器分配给解析器,对文档进行解析,将事件发送给处理器
parser.parse(is, ss);
范例
-
创建一个 空的 Android 项目
cn.twle.android.SaxParser
-
在
app
上点右键,选择Folder - Assets Folder
创建assets
目录,并在assets
目录下新建文件languages.xml
,内容如下<?xml version="1.0" encoding="utf-8" ?> <languages> <language id="1"> <name>Java</name> <rank>1</rank> </language> <language id="2"> <name>C</name> <rank>2</rank> </language> <language id="3"> <name>C++</name> <rank>3</rank> </language> </languages>
-
修改
activity_main.xml
<?xml version="1.0" encoding="utf-8" ?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:padding="8dp" android:orientation="vertical" > <Button android:text="解析" android:id="@+id/btn_parser" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <TextView android:id="@+id/ms_dst" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="" /> </LinearLayout>
-
创建一个 POJO 业务类
Language.java
package cn.twle.android.saxparser; public class Language { private int id; private String name; private String rank; public Language() { } public void setId(int id) { this.id = id; } public int getId() { return this.id; } public void setName(String name) { this.name = name; } public String getName() { return this.name; } public void setRank(String rank) { this.rank = rank; } public String getRank() { return this.rank; } }
-
创建一个
SAX
解析帮助类SaxHelper.java
package cn.twle.android.saxparser; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; import java.util.ArrayList; public class SaxHelper extends DefaultHandler { private Language language; private ArrayList<Language> languages; //当前解析的元素标签 private String tagName = null; /** * 当读取到文档开始标志是触发,通常在这里完成一些初始化操作 */ @Override public void startDocument() throws SAXException { this.languages = new ArrayList<Language>(); } /** * 读到一个开始标签时调用,第二个参数为标签名,最后一个参数为属性数组 */ @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { if (localName.equals("language")) { language = new Language(); language.setId(Integer.parseInt(attributes.getValue("id"))); } this.tagName = localName; } /** * 读到到内容,第一个参数为字符串内容,后面依次为起始位置与长度 */ @Override public void characters(char[] ch, int start, int length) throws SAXException { //判断当前标签是否有效 if (this.tagName != null) { String data = new String(ch, start, length); //读取标签中的内容 if (this.tagName.equals("name")) { this.language.setName(data); } else if (this.tagName.equals("rank")) { this.language.setRank(data); } } } /** * 处理元素结束时触发,这里将对象添加到结合中 */ @Override public void endElement(String uri, String localName, String qName) throws SAXException { if (localName.equals("language")) { this.languages.add(language); language = null; } this.tagName = null; } //获取persons集合 public ArrayList<Language> getData() { return languages; } }
-
修改
MainActivity.java
package cn.twle.android.saxparser; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.Button; import android.widget.TextView; import java.io.InputStream; import java.util.ArrayList; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; public class MainActivity extends AppCompatActivity{ private TextView ms_dst; private Button btn_parser; private ArrayList<Language> languages; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ms_dst = findViewById(R.id.ms_dst); languages = new ArrayList<Language>(); btn_parser = findViewById(R.id.btn_parser); btn_parser.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //获取文件资源建立输入流对象 try { InputStream is = getAssets().open("languages.xml"); SaxHelper saxHelper = new SaxHelper(); SAXParserFactory factory = SAXParserFactory.newInstance(); SAXParser parser = factory.newSAXParser(); parser.parse(is, saxHelper); languages = saxHelper.getData(); is.close(); if (languages.equals(null)) { ms_dst.setText("解析失败"); } StringBuilder sb2 = new StringBuilder(); for (Language p1 : languages) { sb2.append(p1.getId() + ". " + p1.getName() + "的排名是:" + p1.getRank() + "\n"); } ms_dst.setText(sb2.toString()); } catch (Exception e) { e.printStackTrace(); } } }); } }