一、SAX 解析概述
-
SAX(Simple API for XML)是一种基于事件的 XML 解析技术,它一边读取 XML 文件一边解析,占用内存少,适用于大型文件
-
SAX 解析器会触发一系列事件,例如,开始解析元素、结束解析元素、遇到字符数据等,我们只需要实现对应的事件处理器来处理这些事件即可
二、SAX 解析基本使用
1、使用步骤
- 获取解析器实例
SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
SAXParser saxParser = saxParserFactory.newSAXParser();
- 实现对应的事件处理器
class MyHandler extends DefaultHandler {
// 开始解析元素
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
super.startElement(uri, localName, qName, attributes);
}
// 遇到字符数据
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
super.characters(ch, start, length);
}
// 结束解析元素
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
super.endElement(uri, localName, qName);
}
}
- 得到输入源,运行解析器
MyHandler myhandler = new MyHandler();
saxParser.parse(inputSource, myhandler);
2、演示
- user.xml,准备好 XML 文件,该文件放置在
res/raw
目录下,这样,在 Activity 中可通过getResources().openRawResource()
获取到该目录下的资源
<users>
<user>
<name>jack</name>
<age>21</age>
</user>
<user>
<name>tom</name>
<age>22</age>
</user>
</users>
- 事件处理器
class MyHandler extends DefaultHandler {
public final String TAG = MyHandler.class.getSimpleName();
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
Log.i(TAG, "------------------------------ 开始标签:" + qName);
}
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
String str = new String(ch, start, length);
Log.i(TAG, "------------------------------ 文本内容:" + str);
}
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
Log.i(TAG, "------------------------------ 结束标签:" + qName);
}
}
- 测试代码
try {
SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
SAXParser saxParser = saxParserFactory.newSAXParser();
InputStream inputStream = getResources().openRawResource(R.raw.users);
InputSource inputSource = new InputSource(inputStream);
MyHandler myhandler = new MyHandler();
saxParser.parse(inputSource, myhandler);
} catch (ParserConfigurationException | SAXException | IOException e) {
e.printStackTrace();
}
- 输出结果
I/MyHandler: ------------------------------ 开始标签:users
I/MyHandler: ------------------------------ 文本内容:
I/MyHandler: ------------------------------ 文本内容:
I/MyHandler: ------------------------------ 开始标签:user
I/MyHandler: ------------------------------ 文本内容:
I/MyHandler: ------------------------------ 文本内容:
I/MyHandler: ------------------------------ 开始标签:name
I/MyHandler: ------------------------------ 文本内容:jack
I/MyHandler: ------------------------------ 结束标签:name
I/MyHandler: ------------------------------ 文本内容:
I/MyHandler: ------------------------------ 文本内容:
I/MyHandler: ------------------------------ 开始标签:age
I/MyHandler: ------------------------------ 文本内容:21
I/MyHandler: ------------------------------ 结束标签:age
I/MyHandler: ------------------------------ 文本内容:
I/MyHandler: ------------------------------ 文本内容:
I/MyHandler: ------------------------------ 结束标签:user
I/MyHandler: ------------------------------ 文本内容:
I/MyHandler: ------------------------------ 文本内容:
I/MyHandler: ------------------------------ 开始标签:user
I/MyHandler: ------------------------------ 文本内容:
I/MyHandler: ------------------------------ 文本内容:
I/MyHandler: ------------------------------ 开始标签:name
I/MyHandler: ------------------------------ 文本内容:tom
I/MyHandler: ------------------------------ 结束标签:name
I/MyHandler: ------------------------------ 文本内容:
I/MyHandler: ------------------------------ 文本内容:
I/MyHandler: ------------------------------ 开始标签:age
I/MyHandler: ------------------------------ 文本内容:22
I/MyHandler: ------------------------------ 结束标签:age
I/MyHandler: ------------------------------ 文本内容:
I/MyHandler: ------------------------------ 文本内容:
I/MyHandler: ------------------------------ 结束标签:user
I/MyHandler: ------------------------------ 文本内容:
I/MyHandler: ------------------------------ 结束标签:users
3、演示优化
(1)优化思路
-
对于
<name>jack</name>
或<age>21</age>
,我们只关注标签之间的文本内容 -
当 startElement 方法被触发且
qName.equals("name")
时,我们已经到 name 开始标签 -
准备开始获取标签之间的文本内容
-
这个时候在 characters 方法中,即可获取到 name 标签之间的文本内容
-
当 endElement 方法被触发且
qName.equals("name")
时,我们已经到 name 结束标签 -
结束获取标签之间的文本内容
(2)具体实现
- 测试代码,这次我们只关注标签之间的文本内容
class MyHandler extends DefaultHandler {
public final String TAG = MyHandler.class.getSimpleName();
private boolean getTextFlag = false;
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
if (qName.equals("name") || qName.equals("age")) {
getTextFlag = true;
Log.i(TAG, "-------------------- 开始标签:" + qName);
} else if (qName.equals("user")) {
Log.i(TAG, "------------------------------ 开始标签:" + qName);
}
}
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
if (getTextFlag) {
String str = new String(ch, start, length);
Log.i(TAG, "---------- 文本内容:" + str);
}
}
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
if (qName.equals("name") || qName.equals("age")) {
getTextFlag = false;
Log.i(TAG, "-------------------- 结束标签:" + qName);
} else if (qName.equals("user")) {
Log.i(TAG, "------------------------------ 结束标签:" + qName);
}
}
}
try {
InputStream inputStream = getResources().openRawResource(R.raw.users);
InputSource inputSource = new InputSource(inputStream);
SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
SAXParser saxParser = saxParserFactory.newSAXParser();
MyHandler myhandler = new MyHandler();
saxParser.parse(inputSource, myhandler);
} catch (ParserConfigurationException | SAXException | IOException e) {
e.printStackTrace();
}
- 输出结果
I/MyHandler: ------------------------------ 开始标签:user
I/MyHandler: -------------------- 开始标签:name
I/MyHandler: ---------- 文本内容:jack
I/MyHandler: -------------------- 结束标签:name
I/MyHandler: -------------------- 开始标签:age
I/MyHandler: ---------- 文本内容:21
I/MyHandler: -------------------- 结束标签:age
I/MyHandler: ------------------------------ 结束标签:user
I/MyHandler: ------------------------------ 开始标签:user
I/MyHandler: -------------------- 开始标签:name
I/MyHandler: ---------- 文本内容:tom
I/MyHandler: -------------------- 结束标签:name
I/MyHandler: -------------------- 开始标签:age
I/MyHandler: ---------- 文本内容:22
I/MyHandler: -------------------- 结束标签:age
I/MyHandler: ------------------------------ 结束标签:user
三、SAX 解析实例实操
1、案例引入
- 将如下的 XML 文件,希望将它解析成 2 个 User 对象,并放入
List<User> users
集合中
<users>
<user>
<name>jack</name>
<age>21</age>
</user>
<user>
<name>tom</name>
<age>22</age>
</user>
</users>
2、案例思路
(1)创建 User 对象
-
当 startElement 方法被触发且
qName.equals("user")
时,我们已经到 user 开始标签 -
这个时候我们创建 User 对象
(2)获取 name
-
当 startElement 方法被触发且
qName.equals("name")
时,我们已经到 name 开始标签 -
准备开始获取 name 标签之间的文本内容
-
这个时候在 characters 方法中,即可获取到 name 标签之间的文本内容并传递给 User 对象
-
当 endElement 方法被触发且
qName.equals("name")
时,我们已经到 name 结束标签 -
结束获取 name 标签之间的文本内容
(3)获取 age
-
当 startElement 方法被触发且
qName.equals("age")
时,我们已经到 age 开始标签 -
准备开始获取 age 标签之间的文本内容
-
这个时候在 characters 方法中,即可获取到 age 标签之间的文本内容并传递给 User 对象
-
当 endElement 方法被触发且
qName.equals("age")
时,我们已经到 age 结束标签 -
结束获取 age 标签之间的文本内容
(4)保存 User 对象
- 最后我们把 User 对象放入 users 集合中
3、具体实现
(1)Entity
public class User {
private String name;
private Integer age;
public User() {
}
public User(String name, Integer age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
(2)Test Code
class MyHandler extends DefaultHandler {
public final String TAG = MyHandler.class.getSimpleName();
private boolean getNameTextFlag = false;
private boolean getAgeTextFlag = false;
private List<User> users = new ArrayList<>();
private User user = null;
public List<User> getUsers() {
return users;
}
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
if (qName.equals("user")) {
user = new User();
} else if (qName.equals("name")) {
getNameTextFlag = true;
} else if (qName.equals("age")) {
getAgeTextFlag = true;
}
}
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
String str = new String(ch, start, length);
if (getNameTextFlag) {
user.setName(str);
} else if (getAgeTextFlag) {
try {
user.setAge(Integer.parseInt(str));
} catch (NumberFormatException e) {
e.printStackTrace();
}
}
}
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
if (qName.equals("user")) {
users.add(user);
} else if (qName.equals("name")) {
getNameTextFlag = false;
} else if (qName.equals("age")) {
getAgeTextFlag = false;
}
}
}
try {
InputStream inputStream = getResources().openRawResource(R.raw.users);
InputSource inputSource = new InputSource(inputStream);
SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
SAXParser saxParser = saxParserFactory.newSAXParser();
MyHandler myhandler = new MyHandler();
saxParser.parse(inputSource, myhandler);
List<User> users = myhandler.getUsers();
for (User u : users) {
Log.i(TAG, "---------- " + u);
}
} catch (ParserConfigurationException | SAXException | IOException e) {
e.printStackTrace();
}
- 输出结果
I/GetXmlActivity: ---------- User{name='jack', age=21}
I/GetXmlActivity: ---------- User{name='tom', age=22}