本文实例讲述了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程序设计有所帮助。