您的位置:首页 > 移动开发 > Android开发

Android SAX API: XmlResourceParser及其扩展应用

2011-05-23 15:16 330 查看
XmlResourceParser继承了2个接口:AttributeSet和XmlPullParser。其中XmlPullParser定义了Android
SAX框架。跟Java 的SAX API相比,XmlPullParser令人难以置信地简单。

一、使用XmlResourceParser读取资源束中的xml

资源束是应用程序编译后的应用程序包的别称。如果我们有一个xml文件是放在应用程序包内并随编译后的包一起发布的,那么使用XmlResourceParser读取xml非常简单。

首先在res目录下新建目录xml。在xml目录中新建xml文件,例如tqqk.xml:

<?
xml
version
=
"1.0"

encoding
=
"utf-8"

?>

<
tqqk
>



<
item
name
=
"



"

id
=
"78"

/>



<
item
name
=
"

多云

"

id
=
"80"

/>



<
item
name
=
"



"

id
=
"79"

/>



<
item
name
=
"

小(阵)雨

"

id
=
"4"

/>



<
item
name
=
"

中雨

"

id
=
"5"

/>



<
item
name
=
"

大雨

"

id
=
"802"

/>



<
item
name
=
"

大到暴雨

"

id
=
"7"

/>



<
item
name
=
"

雷阵雨

"

id
=
"181"

/>



<
item
name
=
"

雨夹雪

"

id
=
"84"

/>



<
item
name
=
"

小雪

"

id
=
"10"

/>



<
item
name
=
"

中雪

"

id
=
"11"

/>



<
item
name
=
"

大到暴雪

"

id
=
"83"

/>



<
item
name
=
"

冰雹

"

id
=
"13"

/>



<
item
name
=
"



"

id
=
"14"

/>



<
item
name
=
"

多云转晴

"

id
=
"85"

/>



<
item
name
=
"

阴湿

"

id
=
"182"

/>



<
item
name
=
"

闷热

"

id
=
"202"

/>

</
tqqk
>

这是一个天气情况列表,每个item元素有两个属性:name和id。也就是说我们给每种天气定义一个名字和id。

由于我们的这个xml文档中并没有定义DTD或者Schema,Eclipse会提示一个警告,不用理会它。

现在我们需要用 XmlResourceParser 来读取xml文件并转换为KVP(key
value pairs)对象。

新建一个类 KVPsFromXml:

public


class

KVPsFromXml {



private

Context
ctx
;





public


KVPsFromXml(Context c) {




ctx
= c;


}





public

Map<String,
String> TqqkFromXml(String filename) {



Map<String, String> map =
new

HashMap<String,
String>();




//
获得处理
android


xml

文件的
XmlResourceParser
对象



XmlResourceParser xml =
ctx
.getResources().getXml(





getResIDFromXmlFile(filename));




try

{





//
切换到下一个状态,并获得当前状态的类型





int

eventType = xml.next();





while

(
true

) {






//
文档开始状态






if

(eventType ==
XmlPullParser.
START_DOCUMENT

) {





}






//
标签开始状态






else


if

(eventType ==
XmlPullParser.
START_TAG

) {







//
将标签名称和当前标签的深度(根节点的
depth

1
,第
2
层节点的
depth

2
,类推)







switch

(xml.getDepth()){







case

1:








break

;







case

2:








//

item

name

id
属性,并放入
map








String key=xml.getAttributeValue(
null

,
"name"
);







String value=xml.getAttributeValue(
null

,
"id"
);







map.put(key, value);








break

;






}













//
获得当前标签的属性个数

//




int
count =
xml.getAttributeCount();









//
将所有属性的名称和属性值添加到
StringBuffer
对象中

//




for (int
i = 0; i <
count; i++) {

//





sb.append(xml.getAttributeName(i)

//







+
"xml.getAttributeValue(i)");

//




}





}






//
标签结束状态






else


if

(eventType ==
XmlPullParser.
END_TAG

) {





}






//
读取标签内容状态






else


if

(eventType ==
XmlPullParser.
TEXT

) {





}






//
文档结束状态






else


if

(eventType ==
XmlPullParser.
END_DOCUMENT

) {







//
文档分析结束后,退出
while
循环







break

;





}






//
切换到下一个状态,并获得当前状态的类型





eventType
= xml.next();






}



}
catch

(Exception e) {




e.printStackTrace();



}




return

map;


}





public


int


getResIDFromXmlFile(String file) {




int

ret = 0;




@SuppressWarnings
(
"rawtypes"
)



Class c =
null

;






try

{




c = Class.forName
(
"ydtf.ydqx.R$xml"
);




Field field = c.getDeclaredField(file);





if

(field !=
null

)





ret
= field.getInt(c);



}
catch

(Exception ex) {




ex.printStackTrace();



}




return

ret;


}

}

该类的构造函数需要传递一个Context参数,即把使用这个类的Activity引用传递给它。因为Activity有一个很便利的方法getResource可以访问并加载
R 对象中的类(资源)。由于 XmlPullParser接口定义了Android SAX的

XMLPULL V1 API
(
请参考http://www.xmlpull.org

)
,我们可以使用SAX解析中的4个事件:

START_TAG,TEXT,END_TAG,END_DOCUMENT(这跟Java SAX中的4个事件是对应的)。因此在接下来的while循环中,我们针对4个事件进行了分别的处理,从而读取xml中的各个元素及其属性,并组装成KVP(键值对)放入Map中。

ctx
.getResources().getXml().getResIDFromXmlFile())
方法可以获得一个
XmlResourceParser

对象。但是

getResIDFromXmlFile
方法要求提供一个int型的资源id(即R.java中定义的各种16进制数)为参数。

由于res文件夹中的各种资源被映射入R.java的类及字段——具体说,res目录下的子目录映射为R.java中的内部类,子目录中的文件被映射为内部类的字段。因此,xml目录下的tqqk.xml会被映射为R.java中的xml类的tqqk字段。用java表述则是“包名.R.$xml.tqqk”。通过java.reflect包,我们可以得到这个xml文件的资源id。

接下来,我们在Activity中取得xml解析的结果--Map对象:

public


void

onCreate(Bundle
savedInstanceState) {




super

.onCreate(savedInstanceState);



setContentView(R.layout.
ydqxlogin

);




//
read the
xml file:tqqk.xml



KVPsFromXml xml=
new

KVPsFromXml(
this

);




tqqk_map

=xml.TqqkFromXml(
"tqqk"
);



Log.i
(
"tqqk_map"
,
tqqk_map

+
""
);



}

我们可以在LogCat中看到打印出来的结果:


—
·ç
ƒ
­=202, 大å
ˆ
°æ
š
´é
›
¨=7, é
˜
´æ¹¿=182, 大å
ˆ
°æ
š
´é
›
ª=83, å
†
°é
›
¹=13, 中é
›
¨=5, é
›
·é
˜
µé
›
¨=181, 中é
›
ª=11, é
›
¨å¤¹é
›
ª=84, 大é
›
¨=802, é
˜
´=79, é
›
¾=14, å¤
š
äº
‘
=80, å°

é
›
ª=10, æ
™
´=78, å°

ï¼
ˆ
é
˜
括
‰
é
›
¨=4, å¤
š
äº
‘
转æ
™
´=85}

由于name使用了中文,所以出现了乱码。如果我们用adb
logcat命令的话,则可以显示中文:

{大雨=802, 冰雹=13, 雷阵雨=181, 阴湿=182, 晴=78, 阴=79, 闷热=202,
多云=80, 雨夹雪=84, 小(阵)雨=4, 中雨=5, 小雪=10, 大到暴雨=7, 中雪=11, 雾=14, 多云转晴=85, 大到暴雪=83}

二、直接从资源束之外读取xml

有时候,xml并不总是随资源束一起编译,比如说从网络流中获取的xml。

那么我们可以直接使用XmlPullParser接口。

修改KVPsFromXml类的

TqqkFromXml 方法代码:

public

Map<String,
String> TqqkFromXml(InputStream in,String encode){



Map<String, String> map =
new

HashMap<String,
String>();




try

{



XmlPullParserFactory
factory = XmlPullParserFactory.newInstance
();



factory.setNamespaceAware(
true

);



XmlPullParser
xpp = factory.newPullParser();



xpp.setInput(in,encode);






//
切换到下一个状态,并获得当前状态的类型




int

eventType =
xpp.getEventType();






while

(
true

) {







//
文档开始状态







if

(eventType ==
XmlPullParser.
START_DOCUMENT

) {






}







//
标签开始状态







else


if

(eventType ==
XmlPullParser.
START_TAG

) {








//
将标签名称和当前标签的深度(根节点的
depth

1
,第
2
层节点的
depth

2
,类推)








switch

(xpp.getDepth()){








case

1:









break

;








case

2:









//

item

name

id
属性,并放入
map









String
key=xpp.getAttributeValue(
null

,
"name"
);








String
value=xpp.getAttributeValue(
null

,
"id"
);








map.put(key,
value);









break

;







}















//
获得当前标签的属性个数

//





int
count = xml.getAttributeCount();








//
将所有属性的名称和属性值添加到
StringBuffer
对象中

//





for (int
i = 0; i < count; i++) {

//






sb.append(xml.getAttributeName(i)

//








+ "xml.getAttributeValue(i)");

//





}






}







//
标签结束状态







else


if

(eventType ==
XmlPullParser.
END_TAG

) {






}







//
读取标签内容状态







else


if

(eventType ==
XmlPullParser.
TEXT

) {






}







//
文档结束状态







else


if

(eventType ==
XmlPullParser.
END_DOCUMENT

) {








//
文档分析结束后,退出
while
循环








break

;






}







//
切换到下一个状态,并获得当前状态的类型






eventType = xpp.next();





}



}
catch

(Exception e){





e.printStackTrace();



}




return

map;



}

修改Activity调用代码:



String sXml =
"<tqqk>"
+
"<item name=/"

/" id=/"78/"/>"

+
"<item name=/"
多云
/" id=/"80/"/>"

+
"<item name=/"

/" id=/"79/"/>"

+
"<item name=/"
小(阵)雨
/" id=/"4/"/>"

+
"<item name=/"
中雨
/" id=/"5/"/>"

+
"<item name=/"
大雨
/" id=/"802/"/>"

+
"<item name=/"
大到暴雨
/" id=/"7/"/>"

+
"<item name=/"
雷阵雨
/" id=/"181/"/>"

+
"<item name=/"
雨夹雪
/" id=/"84/"/>"

+
"<item name=/"
小雪
/" id=/"10/"/>"

+
"<item name=/"
中雪
/" id=/"11/"/>"

+
"<item name=/"
大到暴雪
/" id=/"83/"/>"

+
"<item name=/"
冰雹
/" id=/"13/"/>"

+
"<item name=/"

/" id=/"14/"/>"

+
"<item name=/"
多云转晴
/" id=/"85/"/>"

+
"<item name=/"
阴湿
/" id=/"182/"/>"

+
"<item name=/"
闷热
/" id=/"202/"/>"
+
"</tqqk>"
;



ByteArrayInputStream stream =
new


ByteArrayInputStream(sXml.getBytes());





KVPsFromXml xml =
new

KVPsFromXml(
this

);




tqqk_map

=
xml.TqqkFromXml(stream,
null

);




Log.i
(
"tqqk_map"
,
tqqk_map

+
""
);

现在我们构建了一个字符流传递给
TqqkFromXml
方法(第二个参数字符编码设定为null,因为java内部字符编码未发生任何改变),它仍然可以为我们读取xml的内容:

{大雨=802, 冰雹=13, 雷阵雨=181, 阴湿=182, 晴=78, 阴=79, 闷热=202,
多云=80, 雨夹雪=84, 小(阵)雨=4, 中雨=5, 小雪=10, 大到暴雨=7, 中雪=11, 雾=14, 多云转晴=85, 大到暴雪=83}

当然,这里我偷了个懒,没有使用网络流读取xml,但结果不会有任何区别。



三、AttributeSet

XmlResourceParser还继承了AttributeSet接口。一个AttributeSet接口对象代表了一个Xml元素,它可以把该元素的所有属性用统一的方法进行访问。正如以下代码所示:

public


Vector<Map<Object,Object>> getAttributeSet(String name){



Vector<Map<Object,Object>>
vector=
new

Vector<Map<Object,Object>>();




//
获得处理
android


xml

文件的
XmlResourceParser
对象



XmlResourceParser parser =
ctx
.getResources().getXml(

getResIDFromXmlFile(name));




int

state = 0;




do

{

//


AttributeSet as;





try

{



state
= parser.next();




if

(state ==
XmlPullParser.
START_TAG

&& parser.getName().equals(
"item"
)) {





AttributeSet as=Xml.asAttributeSet
(parser);






int


n=as.getAttributeCount();





Map<Object,Object> map=
new


HashMap<Object,Object>();








while

(n>0){




n--;




map.put(as.getAttributeName(n),as.getAttributeValue(n));







}








if

(map!=
null

) vector.add(map);



}



}
catch


(XmlPullParserException e1) {



e1.printStackTrace();



}
catch

(IOException e1) {



e1.printStackTrace();



}



}
while

(state !=
XmlPullParser.
END_DOCUMENT

);




return

vector;



}

如你所见,AttributeSet接口实际上是一个抽象的对象,它并没有定义实例变量或字段,它只定义了一系列的访问Xml元素属性的方法,因此我们无法直接保存AttributeSet对象到集合中。最终我们把它复制到
Map 对象并放入集合中(因为我们的xml文件中有多个元素)。

接下来我们打印这些xml元素:

KVPsFromXml xml =
new

KVPsFromXml(
this

);




Vector<Map<Object,Object>> attrs=xml.getAttributeSet(
"tqqk"
);

for

(Map<Object,Object>
as : attrs){




Log.i
(
"map"
,as.toString());

}

当然,Map 距离 Object 已经不远了,要将Map映射为对象只需要使用java的反射机制。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: