本文引见在Android平台中完成对XML的三种剖析体式格局。
XML在种种开辟中都广泛运用,Android也不破例。作为承载数据的一个主要角色,怎样读写XML成为Android开辟中一项主要的妙技。
在Android中,罕见的XML剖析器分别为DOM剖析器、SAX剖析器和PULL剖析器,下面,我将一一向人人细致引见。
第一种体式格局:DOM剖析器:
DOM是基于树形组织的的节点或信息片断的鸠合,许可开辟人员运用DOM API遍历XML树、检索所需数据。剖析该组织平常须要加载悉数文档和组织树形组织,然后才能够检索和更新节点信息。Android完整支撑DOM 剖析。运用DOM中的对象,能够对XML文档举行读取、搜刮、修正、增加和删除等操纵。
DOM的事情道理:运用DOM对XML文件举行操纵时,起首要剖析文件,将文件分为自力的元素、属性和诠释等,然后以节点树的情势在内存中对XML文件举行示意,就能够经由历程节点树接见文档的内容,并依据须要修正文档——这就是DOM的事情道理。
DOM完成时起首为XML文档的剖析定义一组接口,剖析器读入悉数文档,然后组织一个驻留内存的树组织,如许代码就能够运用DOM接口来操纵悉数树组织。
由于DOM在内存中以树形组织寄存,因而检索和更新效力会更高。然则关于迥殊大的文档,剖析和加载悉数文档将会很耗资本。 固然,假如XML文件的内容比较小,采纳DOM是可行的。
经常使用的DoM接口和类:
Document:该接口定义剖析并建立DOM文档的一系列要领,它是文档树的根,是操纵DOM的基础。
Element:该接口继承Node接口,供应了猎取、修正XML元素名字和属性的要领。
Node:该接口供应处置惩罚并猎取节点和子节点值的要领。
NodeList:供应取得节点个数和当前节点的要领。如许就能够迭代地接见各个节点。
DOMParser:该类是Apache的Xerces中的DOM剖析器类,可直接剖析XML文件。
下面是DOM的剖析流程:
第二种体式格局:SAX剖析器:
SAX(Simple API for XML)剖析器是一种基于事宜的剖析器,事宜驱动的流式剖析体式格局是,从文件的最先递次剖析到文档的完毕,不可停息或倒退。它的中心是事宜处置惩罚形式,主如果围绕着事宜源以及事宜处置惩罚器来事情的。当事宜源发生事宜后,挪用事宜处置惩罚器响应的处置惩罚要领,一个事宜就能够获得处置惩罚。在事宜源挪用事宜处置惩罚器中特定要领的时刻,还要传递给事宜处置惩罚器响应事宜的状况信息,如许事宜处置惩罚器才能够依据供应的事宜信息来决议自身的行动。
SAX剖析器的长处是剖析速率快,占用内存少。异常合适在Android挪动装备中运用。
SAX的事情道理:SAX的事情道理简朴地说就是对文档举行递次扫描,当扫描到文档(document)最先与完毕、元素(element)最先与完毕、文档(document)完毕等地方时关照事宜处置惩罚函数,由事宜处置惩罚函数做响应行动,然后继承一样的扫描,直至文档完毕。
在SAX接口中,事宜源是org.xml.sax包中的XMLReader,它经由历程parser()要领来剖析XML文档,并发生事宜。事宜处置惩罚器是org.xml.sax包中ContentHander、DTDHander、ErrorHandler,以及EntityResolver这4个接口。XMLReader经由历程响应事宜处置惩罚器注册要领setXXXX()来完成的与ContentHander、DTDHander、ErrorHandler,以及EntityResolver这4个接口的衔接。
经常使用的SAX接口和类:
Attrbutes:用于获得属性的个数、名字和值。
ContentHandler:定义与文档自身关联的事宜(比方,最先和完毕标记)。大多数运用顺序都注册这些事宜。
DTDHandler:定义与DTD关联的事宜。它没有定义充足的事宜来完整地报告DTD。假如须要对DTD举行语法剖析,请运用可选的DeclHandler。
DeclHandler是SAX的扩大。不是一切的语法剖析器都支撑它。
EntityResolver:定义与装入实体关联的事宜。只要少数几个运用顺序注册这些事宜。
ErrorHandler:定义毛病事宜。很多运用顺序注册这些事宜以便用它们自身的体式格局报错。
DefaultHandler:它供应了这些接LI的缺省完成。在大多数情况下,为运用顺序扩大DefaultHandler并掩盖相干的要领要比直接完成一个接口更轻易。
详见下表:
可知,我们须要XmlReader 以及DefaultHandler来合营剖析xml。
下面是SAX的剖析流程:
第三种体式格局:PULL剖析器:
Android并未供应对Java StAX API的支撑。然则,Android附带了一个pull剖析器,其事情体式格局相似于StAX。它许可用户的运用顺序代码从剖析器中猎取事宜,这与SAX剖析器自动将事宜推入处置惩罚顺序相反。
PULL剖析器的运转体式格局和SAX相似,都是基于事宜的形式。差别的是,在PULL剖析历程当中返回的是数字,且我们须要自身猎取发生的事宜然后做响应的操纵,而不像SAX那样由处置惩罚器触发一种事宜的要领,实行我们的代码。
下面是PULL剖析XML的历程:
读取到xml的声明返回 START_DOCUMENT;
读取到xml的完毕返回 END_DOCUMENT ;
读取到xml的最先标签返回 START_TAG
读取到xml的完毕标签返回 END_TAG
读取到xml的文本返回 TEXT
PULL剖析器玲珑轻巧,剖析速率快,简朴易用,异常合适在Android挪动装备中运用,Android体系内部在剖析种种XML时也是用PULL剖析器,Android官方引荐开辟者们运用Pull剖析手艺。Pull剖析手艺是第三方开辟的开源手艺,它一样能够运用于JavaSE开辟。
PULL 的事情道理:XML pull供应了最先元素和完毕元素。当某个元素最先时,我们能够挪用parser.nextText从XML文档中提取一切字符数据。当诠释到一个文档完毕时,自动生成EndDocument事宜。
经常使用的XML pull的接口和类:
XmlPullParser:XML pull剖析器是一个在XMLPULL VlAP1中供应了定义剖析服从的接口。
XmlSerializer:它是一个接口,定义了XML信息集的序列。
XmlPullParserFactory:这个类用于在XMPULL V1 API中建立XML Pull剖析器。
XmlPullParserException:抛出单一的XML pull剖析器相干的毛病。
PULL的剖析流程以下:
[附加]第四种体式格局: Android.util.Xml类
在Android API中,别的供应了Android.util.Xml类,一样能够剖析XML文件,运用要领相似SAX,也都需编写Handler来处置惩罚XML的剖析,然则在运用上却比SAX来得简朴 ,以下所示:
以android.util.XML完成XML剖析 ,
MyHandler myHandler=new MyHandler0;
android.util.Xm1.parse(ur1.openC0nnection().getlnputStream0,Xm1.Encoding.UTF-8,myHandler);
下面是一个参考文档river.xml,放在assets目次.以下:
<?xml version="1.0" encoding="utf-8"?> <rivers> <river name="灵渠" length="605"> <introduction>
灵渠在广西壮族自治区兴安县境内,是天下上最陈旧的运河之一,有着“天下古代水利修建明珠”的佳誉。灵渠古称秦凿渠、零渠、陡河、兴安运河,于公元前214年凿成通航,距今已2217年,依然发挥着服从。
</ introduction > < imageurl > http://www.ki4.cn/ </ imageurl > </ river > < river name ="胶莱运河" length ="200" > < introduction >
胶莱运河南起黄海灵山海口,北抵渤海三山岛,流经现胶南、胶州、平度、高密、昌邑和莱州等,全长200千米,流域面积达5400平方千米,南北贯串山东半岛,沟通黄渤两海。胶莱运河自平度姚家村东的分水岭南北分流。南流由麻湾口入胶州湾,为南胶莱河,长30千米。北流由海仓口入莱州湾,为北胶莱河,长100余千米。
</ introduction > < imageurl > http://www.ki4.cn/ </ imageurl > </ river > < river name ="苏北浇灌总渠" length ="168" > < introduction >
位于淮河下流江苏省北部,西起洪泽湖边的高良涧,流经洪泽,青浦、淮安,阜宁、射阳,滨海等六县(区),东至扁担口岸入海的大型人工河道。全长168km。
</ introduction > < imageurl > http://www.ki4.cn/ </ imageurl > </ river > </ rivers >
采纳DOM剖析时详细处置惩罚步骤是:
1 起首运用DocumentBuilderFactory建立一个DocumentBuilderFactory实例
2 然后运用DocumentBuilderFactory建立DocumentBuilder
3 然后加载XML文档(Document),
4 然后猎取文档的根结点(Element),
5 然后猎取根结点中一切子节点的列表(NodeList),
6 然后运用再猎取子节点列表中的须要读取的结点。
固然我们视察节点,我须要用一个River对象来保留数据,笼统出River类
public class River implements Serializable { privatestaticfinallong serialVersionUID = 1L; private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getLength() { return length; } public void setLength(int length) { this.length = length; } public String getIntroduction() { return introduction; } public void setIntroduction(String introduction) { this.introduction = introduction; } public String getImageurl() { return imageurl; } public void setImageurl(String imageurl) { this.imageurl = imageurl; } private int length; private String introduction; private String imageurl; }
下面我们就最先读取xml文档对象,并增加进List中:
代码以下: 我们这里是运用assets中的river.xml文件,那末就须要读取这个xml文件,返回输入流。 读取要领为:inputStream=this.context.getResources().getAssets().open(fileName); 参数是xml文件途径,固然默许的是assets目次为根目次。
然后能够用DocumentBuilder对象的parse要领剖析输入流,并返回document对象,然后再遍历doument对象的节点属性。
//猎取悉数河道数据 /** * 参数fileName:为xml文档途径 */ public List<River> getRiversFromXml(String fileName){ List<River> rivers=new ArrayList<River>(); DocumentBuilderFactory factory=null; DocumentBuilder builder=null; Document document=null; InputStream inputStream=null; //起首找到xml文件 factory=DocumentBuilderFactory.newInstance(); try { //找到xml,并加载文档 builder=factory.newDocumentBuilder(); inputStream=this.context.getResources().getAssets().open(fileName); document=builder.parse(inputStream); //找到根Element Element root=document.getDocumentElement(); NodeList nodes=root.getElementsByTagName(RIVER); //遍历根节点一切子节点,rivers 下一切river River river=null; for(int i=0;i<nodes.getLength();i++){ river=new River(); //猎取river元素节点 Element riverElement=(Element)(nodes.item(i)); //猎取river中name属性值 river.setName(riverElement.getAttribute(NAME)); river.setLength(Integer.parseInt(riverElement.getAttribute(LENGTH))); //猎取river下introduction标签 Element introduction=(Element)riverElement.getElementsByTagName(INTRODUCTION).item(0); river.setIntroduction(introduction.getFirstChild().getNodeValue()); Element imageUrl=(Element)riverElement.getElementsByTagName(IMAGEURL).item(0); river.setImageurl(imageUrl.getFirstChild().getNodeValue()); rivers.add(river); } }catch (IOException e){ e.printStackTrace(); } catch (SAXException e) { e.printStackTrace(); } catch (ParserConfigurationException e) { e.printStackTrace(); }finally{ try { inputStream.close(); } catch (IOException e) { e.printStackTrace(); } } return rivers; }
在这里增加到List中, 然后我们运用ListView将他们显示出来。如图所示:
采纳SAX剖析时详细处置惩罚步骤是:
1 建立SAXParserFactory对象
2 依据SAXParserFactory.newSAXParser()要领返回一个SAXParser剖析器
3 依据SAXParser剖析器猎取事宜源对象XMLReader
4 实例化一个DefaultHandler对象
5 衔接事宜源对象XMLReader到事宜处置惩罚类DefaultHandler中
6 挪用XMLReader的parse要领从输入源中猎取到的xml数据
7 经由历程DefaultHandler返回我们须要的数据鸠合。
代码以下:
public List<River> parse(String xmlPath){ List<River> rivers=null; SAXParserFactory factory=SAXParserFactory.newInstance(); try { SAXParser parser=factory.newSAXParser(); //猎取事宜源 XMLReader xmlReader=parser.getXMLReader(); //设置处置惩罚器 RiverHandler handler=new RiverHandler(); xmlReader.setContentHandler(handler); //剖析xml文档 //xmlReader.parse(new InputSource(new URL(xmlPath).openStream())); xmlReader.parse(new InputSource(this.context.getAssets().open(xmlPath))); rivers=handler.getRivers(); } catch (ParserConfigurationException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (SAXException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return rivers; }
重点在于DefaultHandler对象中对每个元素节点,属性,文本内容,文档内容举行处置惩罚。
前面说过DefaultHandler是基于事宜处置惩罚模子的,基础处置惩罚体式格局是:当SAX剖析器导航到文档最先标签时回调startDocument要领,导航到文档完毕标签时回调endDocument要领。当SAX剖析器导航到元素最先标签时回调startElement要领,导航到其文本内容时回调characters要领,导航到标签完毕时回调endElement要领。
依据以上的诠释,我们能够得出以下处置惩罚xml文档逻辑:
1:当导航到文档最先标签时,在回调函数startDocument中,能够不做处置惩罚,固然你能够考证下UTF-8等等。
2:当导航到rivers最先标签时,在回调要领startElement中能够实例化一个鸠合用来存贮list,不过我们这里不必,由于在组织函数中已实例化了。
3:导航到river最先标签时,就申明须要实例化River对象了,固然river标签中另有name ,length属性,因而实例化River后还必须掏出属性值,attributes.getValue(NAME),同时给予river对象中,同时增加为导航到的river标签增加一个boolean为真的标识,用来申明导航到了river元素。
4:固然有river标签内另有子标签(节点),然则SAX剖析器是不晓得导航到什么标签的,它只懂得最先,完毕罢了。那末怎样让它认得我们的各个标签呢?固然须要推断了,因而能够运用回调要领startElement中的参数String localName,把我们的标签字符串与这个参数比较下,就能够了。我们还必须让SAX晓得,如今导航到的是某个标签,因而增加一个true属性让SAX剖析器晓得。
5:它还会导航到文本内标签,(就是<img></img>内里的内容),回调要领characters,我们平常在这个要领中掏出就是<img></img>内里的内容,并保留。 6:固然它是一定会导航到完毕标签</river> 或许</rivers>的,假如是</river>标签,记得把river对象增加进list中。假如是river中的子标签</introduction>,就把前面设置标记导航到这个标签的boolean标记设置为false. 根据以上完成思绪,能够完成以下代码:
/**导航到最先标签触发**/ publicvoid startElement (String uri, String localName, String qName, Attributes attributes){ String tagName=localName.length()!=0?localName:qName; tagName=tagName.toLowerCase().trim(); //假如读取的是river标签最先,则实例化River if(tagName.equals(RIVER)){ isRiver=true; river=new River(); /**导航到river最先节点后**/ river.setName(attributes.getValue(NAME)); river.setLength(Integer.parseInt(attributes.getValue(LENGTH))); } //然后读取其他节点 if(isRiver){ if(tagName.equals(INTRODUCTION)){ xintroduction=true; }else if(tagName.equals(IMAGEURL)){ ximageurl=true; } } } /**导航到完毕标签触发**/ public void endElement (String uri, String localName, String qName){ String tagName=localName.length()!=0?localName:qName; tagName=tagName.toLowerCase().trim(); //假如读取的是river标签完毕,则把River增加进鸠合中 if(tagName.equals(RIVER)){ isRiver=true; rivers.add(river); } //然后读取其他节点 if(isRiver){ if(tagName.equals(INTRODUCTION)){ xintroduction=false; }else if(tagName.equals(IMAGEURL)){ ximageurl=false; } } } //这里是读取到节点内容时刻回调 public void characters (char[] ch, int start, int length){ //设置属性值 if(xintroduction){ //处理null题目 river.setIntroduction(river.getIntroduction()==null?"":river.getIntroduction()+new String(ch,start,length)); }else if(ximageurl){ //处理null题目 river.setImageurl(river.getImageurl()==null?"":river.getImageurl()+new String(ch,start,length)); } }
运转结果跟上例DOM 运转结果雷同。
采纳PULL剖析基础处置惩罚体式格局:
当PULL剖析器导航到文档最先标签时就最先实例化list鸠合用来存贮数据对象。导航到元素最先标签时回推断元素标签范例,假如是river标签,则须要实例化River对象了,假如是其他范例,则取得该标签内容并给予River对象。固然它也会导航到文本标签,不过在这里,我们能够不必。
依据以上的诠释,我们能够得出以下处置惩罚xml文档逻辑:
1:当导航到XmlPullParser.START_DOCUMENT,能够不做处置惩罚,固然你能够实例化鸠合对象等等。
2:当导航到XmlPullParser.START_TAG,则推断是不是是river标签,假如是,则实例化river对象,并挪用getAttributeValue要领猎取标签中属性值。
3:当导航到其他标签,比方Introduction时刻,则推断river对象是不是为空,如不为空,则掏出Introduction中的内容,nextText要领来猎取文本节点内容
4:固然啦,它一定会导航到XmlPullParser.END_TAG的,有最先就要有完毕嘛。在这里我们就须要判读是不是是river完毕标签,假如是,则把river对象存进list鸠合中了,并设置river对象为null.
由以上的处置惩罚逻辑,我们能够得出以下代码:
public List<River> parse(String xmlPath){ List<River> rivers=new ArrayList<River>(); River river=null; InputStream inputStream=null; //取得XmlPullParser剖析器 XmlPullParser xmlParser = Xml.newPullParser(); try { //获得文件流,并设置编码体式格局 inputStream=this.context.getResources().getAssets().open(xmlPath); xmlParser.setInput(inputStream, "utf-8"); //取得剖析到的事宜种别,这里有最先文档,完毕文档,最先标签,完毕标签,文本等等事宜。 int evtType=xmlParser.getEventType(); //一向轮回,直到文档完毕 while(evtType!=XmlPullParser.END_DOCUMENT){ switch(evtType){ case XmlPullParser.START_TAG: String tag = xmlParser.getName(); //假如是river标签最先,则申明须要实例化对象了 if (tag.equalsIgnoreCase(RIVER)) { river = new River(); //掏出river标签中的一些属性值 river.setName(xmlParser.getAttributeValue(null, NAME)); river.setLength(Integer.parseInt(xmlParser.getAttributeValue(null, LENGTH))); }else if(river!=null){ //假如碰到introduction标签,则读取它内容 if(tag.equalsIgnoreCase(INTRODUCTION)){ river.setIntroduction(xmlParser.nextText()); }else if(tag.equalsIgnoreCase(IMAGEURL)){ river.setImageurl(xmlParser.nextText()); } } break; case XmlPullParser.END_TAG: //假如碰到river标签完毕,则把river对象增加进鸠合中 if (xmlParser.getName().equalsIgnoreCase(RIVER) && river != null) { rivers.add(river); river = null; } break; default:break; } //假如xml没有完毕,则导航到下一个river节点 evtType=xmlParser.next(); } } catch (XmlPullParserException e) { // TODO Auto-generated catch block e.printStackTrace(); }catch (IOException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } return rivers; }
运转结果和上面的一样。
几种剖析手艺的比较与总结:
关于Android的挪动装备而言,由于装备的资本比较珍贵,内存是有限的,所以我们须要挑选合适的手艺来剖析XML,如许有利于进步接见的速率。
1 DOM在处置惩罚XML文件时,将XML文件剖析成树状组织并放入内存中举行处置惩罚。当XML文件较小时,我们能够选DOM,由于它简朴、直观。
2 SAX则是以事宜作为剖析XML文件的形式,它将XML文件转化成一系列的事宜,由差别的事宜处置惩罚器来决议怎样处置惩罚。XML文件较大时,挑选SAX手艺是比较合理的。虽然代码量有些大,然则它不须要将一切的XML文件加载到内存中。如许关于有限的Android内存更有用,而且Android供应了一种传统的SAX运用要领以及一个便利的SAX包装器。 运用Android.util.Xml类,从示例中能够看出,会比运用 SAX来得简朴。
3 XML pull剖析并未像SAX剖析那样监听元素的完毕,而是在最先处完成了大部分处置惩罚。这有利于提早读取XML文件,能够极大的削减剖析时候,这类优化关于衔接速率较漫的挪动装备而言尤为主要。关于XML文档较大但只须要文档的一部分时,XML Pull剖析器则是更加有用的要领。
以上就是详解Android完成XML剖析手艺(图)的细致内容,更多请关注ki4网别的相干文章!