一. 前言

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//创建SAXParser工厂
SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
//通过SAXParser工厂创建SAXParser
SAXParser saxParser = saxParserFactory.newSAXParser();
//通过SAXParser获取XMLReader
XMLReader xmlReader = saxParser.getXMLReader();

//实例化自己继承于DefaultHandler的SAXParseHandler,并将其设置为XMLReader的ContentHandler
SAXParseHandler saxParseHandler = new SAXParseHandler();
xmlReader.setContentHandler(saxParseHandler);

//获取xml数据流,并将其转化为InputSource
InputStream is = getResources().openRawResource(R.raw.test);
InputSource inputSource = new InputSource(is);

//使用xmlReader解析Xml文件
xmlReader.parse(inputSource);
saxParseHandler.getXmlList();

二. DefaultHandler重写方法的执行顺序

1. 继承 DefaultHandler 的类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
public class SAXParseHandler extends DefaultHandler {

//日志
public static final String TAG = SAXParseHandler.class.getSimpleName();

//解析的数据集合
private List<WebItem> webItems;

//子项的name
private String ITEM = "item";
private WebItem webItem;

//开始这个文件 test.xml
@Override
public void startDocument() throws SAXException {
super.startDocument();
Log.d(TAG,"startDocument");

webItems = new ArrayList<>();
}

//开始这个元素 item
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
super.startElement(uri, localName, qName, attributes);
Log.d(TAG,"startElement localName:"+localName+" qName:"+qName);

//new 一个模型类
webItem = new WebItem();

//如果元素名相同
if (TextUtils.equals(localName, ITEM)){
for (int i = 0; i < attributes.getLength(); i++) {
//循环读取属性 id
if (TextUtils.equals(attributes.getLocalName(i),"id")){
webItem.setId(Integer.parseInt(attributes.getValue(i)));
}

//循环读取属性 url
if (TextUtils.equals(attributes.getLocalName(i),"url")){
webItem.setUrl(attributes.getValue(i));
}
}
}
}

//内容 百度
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
super.characters(ch, start, length);

//获取内容
String content = String.valueOf(ch, start, length);
Log.d(TAG,"characters ch:"+content);
}

//结束这个元素 item
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
super.endElement(uri, localName, qName);
Log.d(TAG,"endElement localName:"+localName+" qName:"+qName);

webItems.add(webItem);
}

//结束这个文件 test.xml
@Override
public void endDocument() throws SAXException {
super.endDocument();
Log.d(TAG,"endDocument");
}

//返回数据
public List<WebItem> getXmlList(){
return webItems;
}
}

2. 根据xml文件的内容将方法执行顺序输入到日志中

① 只有一个根节点

1
2
3
4
5
6
7
8
9
10
11
12
13
//待解析的Xml文件
<?xml version="1.0" encoding="utf-8"?>
<web></web>

//解析结果
startDocument
startElement localName:web qName:web
characters ch:
characters ch:

characters ch:
endElement localName:web qName:web
endDocument

② 根节点下有一个非根节点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//待解析的Xml文件
<?xml version="1.0" encoding="utf-8"?>
<web>
<item id="0" url="https://www.baidu.com">百度</item>
</web>

//解析结果
startDocument
startElement localName:web qName:web
characters ch:
characters ch:

startElement localName:item qName:item
characters ch:百度
endElement localName:item qName:item
characters ch:
characters ch:

characters ch:
endElement localName:web qName:web
endDocument

③ 根节点下有两个非根节点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//待解析的Xml文件
<?xml version="1.0" encoding="utf-8"?>
<web>
<item id="0" url="https://www.baidu.com">百度</item>
<item id="1" url="https://www.taobao.com">淘宝</item>
</web>

//解析结果
startDocument
startElement localName:web qName:web
characters ch:
characters ch:

startElement localName:item qName:item
characters ch:百度
endElement localName:item qName:item
characters ch:
characters ch:

startElement localName:item qName:item
characters ch:淘宝
endElement localName:item qName:item
characters ch:
characters ch:

characters ch:
endElement localName:web qName:web
endDocument

④ 根节点下有三个非根节点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
//待解析的Xml文件
<?xml version="1.0" encoding="utf-8"?>
<web>
<item id="0" url="https://www.baidu.com">百度</item>
<item id="1" url="https://www.taobao.com">淘宝</item>
<item id="2" url="https://www.qq.com">腾讯</item>
</web>

//解析结果
startDocument
startElement localName:web qName:web
characters ch:
characters ch:

startElement localName:item qName:item
characters ch:百度
endElement localName:item qName:item
characters ch:
characters ch:

startElement localName:item qName:item
characters ch:淘宝
endElement localName:item qName:item
characters ch:
characters ch:

startElement localName:item qName:item
characters ch:腾讯
endElement localName:item qName:item

characters ch:
endElement localName:web qName:web
endDocument

⑤ 根节点下有四个非根节点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
//待解析的Xml文件
<?xml version="1.0" encoding="utf-8"?>
<web>
<item id="0" url="https://www.baidu.com">百度</item>
<item id="1" url="https://www.taobao.com">淘宝</item>
<item id="2" url="https://www.qq.com">腾讯</item>
<item id="3" url="https://www.aili.com">阿里巴巴</item>
</web>

//解析结果
startDocument
startElement localName:web qName:web
characters ch:
characters ch:

startElement localName:item qName:item
characters ch:百度
endElement localName:item qName:item
characters ch:
characters ch:

startElement localName:item qName:item
characters ch:淘宝
endElement localName:item qName:item
characters ch:
characters ch:

startElement localName:item qName:item
characters ch:腾讯
endElement localName:item qName:item
characters ch:
characters ch:

startElement localName:item qName:item
characters ch:阿里巴巴
endElement localName:item qName:item

characters ch:
endElement localName:web qName:web
endDocument

⑥ 总结规律

如果是根节点,那么一定会执行以下的方法:

1
2
3
4
5
6
7
8
startDocument
startElement localName:web qName:web
characters ch:
characters ch:

characters ch:
endElement localName:web qName:web
endDocument

如果是非根节点,且根节点下的非根节点数 <= 两个,对于非根节点一定会执行以下的方法:

1
2
3
4
5
startElement localName:item qName:item
characters ch:内容值
endElement localName:item qName:item
characters ch:
characters ch:

如果是非根节点,且根节点下的非根节点数 > 两个,对于非根节点一定会执行以下的方法:

1
2
3
4
5
6
7
8
9
10
11
//最后一个根节点执行:
startElement localName:item qName:item
characters ch:内容值
endElement localName:item qName:item

//其他根节点执行:
startElement localName:item qName:item
characters ch:内容值
endElement localName:item qName:item
characters ch:
characters ch:

三. 实际小例子

1. 继承于DefaultHandler的SAXParseHandler

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
public class SAXParseHandler extends DefaultHandler {

//日志
public static final String TAG = SAXParseHandler.class.getSimpleName();

//解析的数据集合
private List<WebItem> webItems;

//子项的name
private String NODE_NAME = "item";

//模型类
private WebItem webItem;

//当前读取的node_name的值
private String current_node_name;

//开始这个文件 test.xml
@Override
public void startDocument() throws SAXException {
super.startDocument();
Log.d(TAG,"startDocument");

webItems = new ArrayList<>();
}

//开始这个元素 item
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
super.startElement(uri, localName, qName, attributes);
Log.d(TAG,"startElement localName:"+localName+" qName:"+qName);

//new 一个模型类
webItem = new WebItem();

//如果元素名相同
if (TextUtils.equals(localName, NODE_NAME)){
//设置当前的node_name
current_node_name = localName;

//读取属性
for (int i = 0; i < attributes.getLength(); i++) {
//循环读取属性 id
if (TextUtils.equals(attributes.getLocalName(i),"id")){
webItem.setId(Integer.parseInt(attributes.getValue(i)));
}

//循环读取属性 url
if (TextUtils.equals(attributes.getLocalName(i),"url")){
webItem.setUrl(attributes.getValue(i));
}
}
}
}

//内容 百度
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
super.characters(ch, start, length);

//获取内容
String content = String.valueOf(ch, start, length);
Log.d(TAG,"characters ch:"+content);

//设置
if (TextUtils.equals(current_node_name,NODE_NAME)){
webItem.setContent(content);
}
}

//结束这个元素 item
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
super.endElement(uri, localName, qName);
Log.d(TAG,"endElement localName:"+localName+" qName:"+qName);

if (TextUtils.equals(current_node_name,NODE_NAME)){
//将读取的数据加入到集合中
webItems.add(webItem);
}

//取消当前的node_name
current_node_name = null;
}

//结束这个文件 test.xml
@Override
public void endDocument() throws SAXException {
super.endDocument();
Log.d(TAG,"endDocument");
}

//返回数据
public List<WebItem> getXmlList(){
return webItems;
}
}

2. 待解析的Xml文件

1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0" encoding="utf-8"?>
<web>
<item id="0" url="https://www.baidu.com">百度</item>
<item id="1" url="https://www.taobao.com">淘宝</item>
<item id="2" url="https://www.qq.com">腾讯</item>
<item id="3" url="https://www.aili.com">阿里巴巴</item>
<item id="4" url="https://www.taobao.com">淘宝</item>
<item id="5" url="https://www.wechat.com">微信</item>
<item id="6" url="https://www.timi.com">天美</item>
<item id="7" url="https://www.guangzi.com">光子</item>
<item id="8" url="https://www.taoken.com">字节跳动</item>
<item id="9" url="https://www.douyin.com">抖音</item>
</web>

3. Activity中的调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

try {
//解析Xml数据
testSexParse();
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}

/**
* Sex方式解析
*/
private void testSexParse() throws ParserConfigurationException, SAXException, IOException {
/*SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
SAXParser saxParser = saxParserFactory.newSAXParser();
XMLReader xmlReader = saxParser.getXMLReader();

SAXParseHandler saxParseHandler = new SAXParseHandler();
xmlReader.setContentHandler(saxParseHandler);

InputStream is = getResources().openRawResource(R.raw.test);
InputSource inputSource = new InputSource(is);

xmlReader.parse(inputSource);
saxParseHandler.getXmlList();*/

XMLReader xmlReader = SAXParserFactory.newInstance().newSAXParser().getXMLReader();
xmlReader.setContentHandler(new SAXParseHandler());
xmlReader.parse(new InputSource(getResources().openRawResource(R.raw.test)));

SAXParseHandler saxParseHandler = (SAXParseHandler) xmlReader.getContentHandler();
for (WebItem webItem : saxParseHandler.getXmlList()) {
//Toast.makeText(this, "id:"+webItem.getId()+" url:"+webItem.getUrl()+" content:"+webItem.getContent(), Toast.LENGTH_SHORT).show();
//显示数据
addToRootLayout((LinearLayout) findViewById(R.id.root),webItem);
}
}

/**
* 将WebItem显示到布局中
* @param layout
* @param webItem
*/
private void addToRootLayout(LinearLayout layout, WebItem webItem){
View inflate = LayoutInflater.from(this).inflate(R.layout.web_item_layout, null);
TextView id_text = inflate.findViewById(R.id.id);
TextView content_text = inflate.findViewById(R.id.content);
TextView url_text = inflate.findViewById(R.id.url);

id_text.setText(String.valueOf(webItem.getId()));
content_text.setText(webItem.getContent());
url_text.setText(webItem.getUrl());

layout.addView(inflate);
}
}

4. 运行结果

5. 源码地址

ParseXml

参考文章

sax中DefaultHander解析xml过程和先后顺序