Android编程解析XML方法详解(SAX,DOM与PULL)
本文实例讲述了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,内容如下:
<?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的代码:
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结构跟原始文档略有不同,是下面这种格式:
<?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代码如下:
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代码如下:
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中完成:
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代码如下:
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中只需改一个地方:
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代码如下:
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做以下更改:
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程序设计有所帮助。