本文实例讲述了android编程解析xml方法。分享给大家供大家参考,具体如下:
xml在各种开发中都广泛应用,android也不例外。作为承载数据的一个重要角色,如何读写xml成为android开发中一项重要的技能。今天就由我向大家介绍一下在android平台下几种常见的xml解析和创建的方法。
在android中,常见的xml解析器分别为sax解析器、dom解析器和pull解析器,下面,我将一一向大家详细介绍。
sax解析器:
sax(simple api for xml)解析器是一种基于事件的解析器,它的核心是事件处理模式,主要是围绕着事件源以及事件处理器来工作的。当事件源产生事件后,调用事件处理器相应的处理方法,一个事件就可以得到处理。在事件源调用事件处理器中特定方法的时候,还要传递给事件处理器相应事件的状态信息,这样事件处理器才能够根据提供的事件信息来决定自己的行为。
sax解析器的优点是解析速度快,占用内存少。非常适合在android移动设备中使用。
dom解析器:
dom是基于树形结构的的节点或信息片段的集合,允许开发人员使用dom api遍历xml树、检索所需数据。分析该结构通常需要加载整个文档和构造树形结构,然后才可以检索和更新节点信息。
由于dom在内存中以树形结构存放,因此检索和更新效率会更高。但是对于特别大的文档,解析和加载整个文档将会很耗资源。
pull解析器:
pull解析器的运行方式和sax类似,都是基于事件的模式。不同的是,在pull解析过程中,我们需要自己获取产生的事件然后做相应的操作,而不像sax那样由处理器触发一种事件的方法,执行我们的代码。pull解析器小巧轻便,解析速度快,简单易用,非常适合在android移动设备中使用,android系统内部在解析各种xml时也是用pull解析器。
以上三种解析器,都是非常实用的解析器,我将会一一介绍。我们将会使用这三种解析技术完成一项共同的任务。
我们新建一个项目,项目结构如下:
我会在项目的assets目录中放置一个xml文档books.xml,内容如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
<?xml version= "1.0" encoding= "utf-8" ?>
<books>
<book>
<id> 1001 </id>
<name>thinking in java</name>
<price> 80.00 </price>
</book>
<book>
<id> 1002 </id>
<name>core java</name>
<price> 90.00 </price>
</book>
<book>
<id> 1003 </id>
<name>hello, andriod</name>
<price> 100.00 </price>
</book>
</books>
|
然后我们分别使用以上三种解析技术解析文档,得到一个list<book>的对象,先来看一下book.java的代码:
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
|
package com.scott.xml.model;
public class book {
private int id;
private string name;
private float price;
public int getid() {
return id;
}
public void setid( int id) {
this .id = id;
}
public string getname() {
return name;
}
public void setname(string name) {
this .name = name;
}
public float getprice() {
return price;
}
public void setprice( float price) {
this .price = price;
}
@override
public string tostring() {
return "id:" + id + ", name:" + name + ", price:" + price;
}
}
|
最后,我们还要把这个集合对象中的数据生成一个新的xml文档,如图:
生成的xml结构跟原始文档略有不同,是下面这种格式:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
<?xml version= "1.0" encoding= "utf-8" ?>
<books>
<book id= "1001" >
<name>thinking in java</name>
<price> 80.0 </price>
</book>
<book id= "1002" >
<name>core java</name>
<price> 90.0 </price>
</book>
<book id= "1003" >
<name>hello, andriod</name>
<price> 100.0 </price>
</book>
</books>
|
接下来,就该介绍操作过程了,我们先为解析器定义一个bookparser接口,每种类型的解析器需要实现此接口。bookparser.java代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
package com.scott.xml.parser;
import java.io.inputstream;
import java.util.list;
import com.scott.xml.model.book;
public interface bookparser {
/**
* 解析输入流 得到book对象集合
* @param is
* @return
* @throws exception
*/
public list<book> parse(inputstream is) throws exception;
/**
* 序列化book对象集合 得到xml形式的字符串
* @param books
* @return
* @throws exception
*/
public string serialize(list<book> books) throws exception;
}
|
好了,我们就该一个一个的实现该接口,完成我们的解析过程。
使用sax解析器:
saxbookparser.java代码如下:
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
98
99
100
101
102
103
104
105
106
|
package com.scott.xml.parser;
import java.io.inputstream;
import java.io.stringwriter;
import java.util.arraylist;
import java.util.list;
import javax.xml.parsers.saxparser;
import javax.xml.parsers.saxparserfactory;
import javax.xml.transform.outputkeys;
import javax.xml.transform.result;
import javax.xml.transform.transformer;
import javax.xml.transform.transformerfactory;
import javax.xml.transform.sax.saxtransformerfactory;
import javax.xml.transform.sax.transformerhandler;
import javax.xml.transform.stream.streamresult;
import org.xml.sax.attributes;
import org.xml.sax.saxexception;
import org.xml.sax.helpers.attributesimpl;
import org.xml.sax.helpers.defaulthandler;
import com.scott.xml.model.book;
public class saxbookparser implements bookparser {
@override
public list<book> parse(inputstream is) throws exception {
saxparserfactory factory = saxparserfactory.newinstance(); //取得saxparserfactory实例
saxparser parser = factory.newsaxparser(); //从factory获取saxparser实例
myhandler handler = new myhandler(); //实例化自定义handler
parser.parse(is, handler); //根据自定义handler规则解析输入流
return handler.getbooks();
}
@override
public string serialize(list<book> books) throws exception {
saxtransformerfactory factory = (saxtransformerfactory) transformerfactory.newinstance(); //取得saxtransformerfactory实例
transformerhandler handler = factory.newtransformerhandler(); //从factory获取transformerhandler实例
transformer transformer = handler.gettransformer(); //从handler获取transformer实例
transformer.setoutputproperty(outputkeys.encoding, "utf-8" ); // 设置输出采用的编码方式
transformer.setoutputproperty(outputkeys.indent, "yes" ); // 是否自动添加额外的空白
transformer.setoutputproperty(outputkeys.omit_xml_declaration, "no" ); // 是否忽略xml声明
stringwriter writer = new stringwriter();
result result = new streamresult(writer);
handler.setresult(result);
string uri = "" ; //代表命名空间的uri 当uri无值时 须置为空字符串
string localname = "" ; //命名空间的本地名称(不包含前缀) 当没有进行命名空间处理时 须置为空字符串
handler.startdocument();
handler.startelement(uri, localname, "books" , null );
attributesimpl attrs = new attributesimpl(); //负责存放元素的属性信息
char [] ch = null ;
for (book book : books) {
attrs.clear(); //清空属性列表
attrs.addattribute(uri, localname, "id" , "string" , string.valueof(book.getid())); //添加一个名为id的属性(type影响不大,这里设为string)
handler.startelement(uri, localname, "book" , attrs); //开始一个book元素 关联上面设定的id属性
handler.startelement(uri, localname, "name" , null ); //开始一个name元素 没有属性
ch = string.valueof(book.getname()).tochararray();
handler.characters(ch, 0 , ch.length); //设置name元素的文本节点
handler.endelement(uri, localname, "name" );
handler.startelement(uri, localname, "price" , null ); //开始一个price元素 没有属性
ch = string.valueof(book.getprice()).tochararray();
handler.characters(ch, 0 , ch.length); //设置price元素的文本节点
handler.endelement(uri, localname, "price" );
handler.endelement(uri, localname, "book" );
}
handler.endelement(uri, localname, "books" );
handler.enddocument();
return writer.tostring();
}
//需要重写defaulthandler的方法
private class myhandler extends defaulthandler {
private list<book> books;
private book book;
private stringbuilder builder;
//返回解析后得到的book对象集合
public list<book> getbooks() {
return books;
}
@override
public void startdocument() throws saxexception {
super .startdocument();
books = new arraylist<book>();
builder = new stringbuilder();
}
@override
public void startelement(string uri, string localname, string qname, attributes attributes) throws saxexception {
super .startelement(uri, localname, qname, attributes);
if (localname.equals( "book" )) {
book = new book();
}
builder.setlength( 0 ); //将字符长度设置为0 以便重新开始读取元素内的字符节点
}
@override
public void characters( char [] ch, int start, int length) throws saxexception {
super .characters(ch, start, length);
builder.append(ch, start, length); //将读取的字符数组追加到builder中
}
@override
public void endelement(string uri, string localname, string qname) throws saxexception {
super .endelement(uri, localname, qname);
if (localname.equals( "id" )) {
book.setid(integer.parseint(builder.tostring()));
} else if (localname.equals( "name" )) {
book.setname(builder.tostring());
} else if (localname.equals( "price" )) {
book.setprice( float .parsefloat(builder.tostring()));
} else if (localname.equals( "book" )) {
books.add(book);
}
}
}
}
|
代码中,我们定义了自己的事件处理逻辑,重写了defaulthandler的几个重要的事件方法。下面我为大家着重介绍一下defaulthandler的相关知识。defaulthandler是一个事件处理器,可以接收解析器报告的所有事件,处理所发现的数据。它实现了entityresolver接口、dtdhandler接口、errorhandler接口和contenthandler接口。这几个接口代表不同类型的事件处理器。我们着重介绍一下contenthandler接口。结构如图:
这几个比较重要的方法已被我用红线标注,defaulthandler实现了这些方法,但在方法体内没有做任何事情,因此我们在使用时必须覆写相关的方法。最重要的是startelement方法、characters方法和endelement方法。当执行文档时遇到起始节点,startelement方法将会被调用,我们可以获取起始节点相关信息;然后characters方法被调用,我们可以获取节点内的文本信息;最后endelement方法被调用,我们可以做收尾的相关操作。
最后,我们需要调用sax解析程序,这个步骤在mainactivity中完成:
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
|
package com.scott.xml;
import java.io.fileoutputstream;
import java.io.inputstream;
import java.util.list;
import android.app.activity;
import android.content.context;
import android.os.bundle;
import android.util.log;
import android.view.view;
import android.widget.button;
import com.scott.xml.model.book;
import com.scott.xml.parser.bookparser;
import com.scott.xml.parser.saxbookparser;
public class mainactivity extends activity {
private static final string tag = "xml" ;
private bookparser parser;
private list<book> books;
@override
public void oncreate(bundle savedinstancestate) {
super .oncreate(savedinstancestate);
setcontentview(r.layout.main);
button readbtn = (button) findviewbyid(r.id.readbtn);
button writebtn = (button) findviewbyid(r.id.writebtn);
readbtn.setonclicklistener( new view.onclicklistener() {
@override
public void onclick(view v) {
try {
inputstream is = getassets().open( "books.xml" );
parser = new saxbookparser(); //创建saxbookparser实例
books = parser.parse(is); //解析输入流
for (book book : books) {
log.i(tag, book.tostring());
}
} catch (exception e) {
log.e(tag, e.getmessage());
}
}
});
writebtn.setonclicklistener( new view.onclicklistener() {
@override
public void onclick(view v) {
try {
string xml = parser.serialize(books); //序列化
fileoutputstream fos = openfileoutput( "books.xml" , context.mode_private);
fos.write(xml.getbytes( "utf-8" ));
} catch (exception e) {
log.e(tag, e.getmessage());
}
}
});
}
}
|
界面就两个按钮,顺便给大家贴上:
点击“readxml”按钮,将会调用sax解析器解析文档,并在日志台打印相关信息:
然后再点击“writexml”按钮,将会在该应用包下的files目录生成一个books.xml文件:
使用dom解析器:
dombookparser.java代码如下:
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
|
package com.scott.xml.parser;
import java.io.inputstream;
import java.io.stringwriter;
import java.util.arraylist;
import java.util.list;
import javax.xml.parsers.documentbuilder;
import javax.xml.parsers.documentbuilderfactory;
import javax.xml.transform.outputkeys;
import javax.xml.transform.result;
import javax.xml.transform.source;
import javax.xml.transform.transformer;
import javax.xml.transform.transformerfactory;
import javax.xml.transform.dom.domsource;
import javax.xml.transform.stream.streamresult;
import org.w3c.dom.document;
import org.w3c.dom.element;
import org.w3c.dom.node;
import org.w3c.dom.nodelist;
import com.scott.xml.model.book;
public class dombookparser implements bookparser {
@override
public list<book> parse(inputstream is) throws exception {
list<book> books = new arraylist<book>();
documentbuilderfactory factory = documentbuilderfactory.newinstance(); //取得documentbuilderfactory实例
documentbuilder builder = factory.newdocumentbuilder(); //从factory获取documentbuilder实例
document doc = builder.parse(is); //解析输入流 得到document实例
element rootelement = doc.getdocumentelement();
nodelist items = rootelement.getelementsbytagname( "book" );
for ( int i = 0 ; i < items.getlength(); i++) {
book book = new book();
node item = items.item(i);
nodelist properties = item.getchildnodes();
for ( int j = 0 ; j < properties.getlength(); j++) {
node property = properties.item(j);
string nodename = property.getnodename();
if (nodename.equals( "id" )) {
book.setid(integer.parseint(property.getfirstchild().getnodevalue()));
} else if (nodename.equals( "name" )) {
book.setname(property.getfirstchild().getnodevalue());
} else if (nodename.equals( "price" )) {
book.setprice( float .parsefloat(property.getfirstchild().getnodevalue()));
}
}
books.add(book);
}
return books;
}
@override
public string serialize(list<book> books) throws exception {
documentbuilderfactory factory = documentbuilderfactory.newinstance();
documentbuilder builder = factory.newdocumentbuilder();
document doc = builder.newdocument(); //由builder创建新文档
element rootelement = doc.createelement( "books" );
for (book book : books) {
element bookelement = doc.createelement( "book" );
bookelement.setattribute( "id" , book.getid() + "" );
element nameelement = doc.createelement( "name" );
nameelement.settextcontent(book.getname());
bookelement.appendchild(nameelement);
element priceelement = doc.createelement( "price" );
priceelement.settextcontent(book.getprice() + "" );
bookelement.appendchild(priceelement);
rootelement.appendchild(bookelement);
}
doc.appendchild(rootelement);
transformerfactory transfactory = transformerfactory.newinstance(); //取得transformerfactory实例
transformer transformer = transfactory.newtransformer(); //从transfactory获取transformer实例
transformer.setoutputproperty(outputkeys.encoding, "utf-8" ); // 设置输出采用的编码方式
transformer.setoutputproperty(outputkeys.indent, "yes" ); // 是否自动添加额外的空白
transformer.setoutputproperty(outputkeys.omit_xml_declaration, "no" ); // 是否忽略xml声明
stringwriter writer = new stringwriter();
source source = new domsource(doc); //表明文档来源是doc
result result = new streamresult(writer); //表明目标结果为writer
transformer.transform(source, result); //开始转换
return writer.tostring();
}
}
|
然后再mainactivity中只需改一个地方:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
readbtn.setonclicklistener( new view.onclicklistener() {
@override
public void onclick(view v) {
try {
inputstream is = getassets().open( "books.xml" );
// parser = new saxbookparser();
parser = new dombookparser();
books = parser.parse(is);
for (book book : books) {
log.i(tag, book.tostring());
}
} catch (exception e) {
log.e(tag, e.getmessage());
}
}
);
|
执行结果是一样的。
使用pull解析器:
pullbookparser.java代码如下:
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
|
package com.scott.xml.parser;
import java.io.inputstream;
import java.io.stringwriter;
import java.util.arraylist;
import java.util.list;
import org.xmlpull.v1.xmlpullparser;
import org.xmlpull.v1.xmlserializer;
import android.util.xml;
import com.scott.xml.model.book;
public class pullbookparser implements bookparser {
@override
public list<book> parse(inputstream is) throws exception {
list<book> books = null ;
book book = null ;
// xmlpullparserfactory factory = xmlpullparserfactory.newinstance();
// xmlpullparser parser = factory.newpullparser();
xmlpullparser parser = xml.newpullparser(); //由android.util.xml创建一个xmlpullparser实例
parser.setinput(is, "utf-8" ); //设置输入流 并指明编码方式
int eventtype = parser.geteventtype();
while (eventtype != xmlpullparser.end_document) {
switch (eventtype) {
case xmlpullparser.start_document:
books = new arraylist<book>();
break ;
case xmlpullparser.start_tag:
if (parser.getname().equals( "book" )) {
book = new book();
} else if (parser.getname().equals( "id" )) {
eventtype = parser.next();
book.setid(integer.parseint(parser.gettext()));
} else if (parser.getname().equals( "name" )) {
eventtype = parser.next();
book.setname(parser.gettext());
} else if (parser.getname().equals( "price" )) {
eventtype = parser.next();
book.setprice( float .parsefloat(parser.gettext()));
}
break ;
case xmlpullparser.end_tag:
if (parser.getname().equals( "book" )) {
books.add(book);
book = null ;
}
break ;
}
eventtype = parser.next();
}
return books;
}
@override
public string serialize(list<book> books) throws exception {
// xmlpullparserfactory factory = xmlpullparserfactory.newinstance();
// xmlserializer serializer = factory.newserializer();
xmlserializer serializer = xml.newserializer(); //由android.util.xml创建一个xmlserializer实例
stringwriter writer = new stringwriter();
serializer.setoutput(writer); //设置输出方向为writer
serializer.startdocument( "utf-8" , true );
serializer.starttag( "" , "books" );
for (book book : books) {
serializer.starttag( "" , "book" );
serializer.attribute( "" , "id" , book.getid() + "" );
serializer.starttag( "" , "name" );
serializer.text(book.getname());
serializer.endtag( "" , "name" );
serializer.starttag( "" , "price" );
serializer.text(book.getprice() + "" );
serializer.endtag( "" , "price" );
serializer.endtag( "" , "book" );
}
serializer.endtag( "" , "books" );
serializer.enddocument();
return writer.tostring();
}
}
|
然后再对mainactivity做以下更改:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
readbtn.setonclicklistener( new view.onclicklistener() {
@override
public void onclick(view v) {
try {
inputstream is = getassets().open( "books.xml" );
// parser = new saxbookparser();
// parser = new dombookparser();
parser = new pullbookparser();
books = parser.parse(is);
for (book book : books) {
log.i(tag, book.tostring());
}
} catch (exception e) {
log.e(tag, e.getmessage());
}
}
});
|
和其他两个执行结果都一样。
对于这三种解析器各有优点,我个人比较倾向于pull解析器,因为sax解析器操作起来太笨重,dom不适合文档较大,内存较小的场景,唯有pull轻巧灵活,速度快,占用内存小,使用非常顺手。读者也可以根据自己的喜好选择相应的解析技术。
希望本文所述对大家android程序设计有所帮助。