第五章 以数据为中心—数据存取(2)

5.1.2结构性的文件—读写XML文件

通过上面的介绍我们可以自由的操作Android中普通的文本文件了,下面我们来介绍下Android中比较常用的结构性的文件—XML文件。

XML,可扩展标记语言(Extensible Markup Language),用于标记电子文件,使其具有结构性的标记语言,可以用来标记数据、定义数据类型,是一种允许用户对自己的标记语言进行定义的源语言。

AndroidSDK提供了如下package来支持XML的读写:

l  javax.xml  根据XML规范定义核心XML常量和功能。

l  javax.xml.parsers  提供DOM和SAX方法解析XML文档。

l  org.w3c.dom  W3C提供的使用DOM方法读取XML。

l  org.xml.sax  提供核心SAX APIs。

l  org.xmlpull.v1  PULL解析器。

后面三个包中分别是android自带的三个XML解析器,有PULL、SAX(Simple API for XML)、DOM解析器。其中PULL跟SAX都是以事件作为驱动导向的解析器,优点是占用内存小,处理速度快。DOM是将整个XML放入内存中再解析,处理速度要稍差一些,但DOM也有自己的优点,可以在解析的时候适当增加节点。

在这里对这3种方法分别加以说明。

首先我们来看看我们需要解析的示例XML文档:


<?xml version="1.0" encoding="UTF-8"?>

<persons>

<person id = "1">

<name>小王</name>

<age>20</age>

</person>

<person id = "2">

<name>小明</name>

<age>30</age>

</person>

<person id = "3">

<name>小丽</name>

<age>40</age>

</person>

</persons>

--------------------------------------------

程序员赚钱不易 一定要学会理财

平安陆金所 隶属于平安集团的p2p平台

年投资回报率7%-9% 是替代银行理财的首选

个人经验 推荐投资安鑫或者有担保的彩虹项目

不要投资安e 那个几乎无法转让 想提前提现非常困难

网站链接 http://affiliate.lufax.com/action/36XBU

首次投资1000元即可额外赚几百元 不赚白不赚

--------------------------------------------

然后我们在代码中创建一个与XML子节点对应的模型类。


public class Person {

protected String id;

protected String name;

protected String age;

public String getId() {

return id;

}

public void setId(String id) {

this.id = id;

}

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

public String getAge() {

return age;

}

public void setAge(String age) {

this.age = age;

}

}

下面我们就将对这个XML用不同的方法来解析。

先来看如何采用DOM方式进行解析。

采用DOM的方法,读取XML文档的思路,这基本上与XML的结构是完全一样的。首先加载XML文档(Document),然后获取文档的根结点(Element),然后获取根结点中所有子节点的列表(NodeList),然后使用再获取子节点列表中的需要读取的结点。

根据以上思路,简要写个读取XML文件的方法如下:

1)实现DomHandler。


// import略

public class DomHandler {

private InputStream input;

private List<Person> persons;

private Person person;

public DomHandler() {

}

public DomHandler(InputStream input) {

this.input = input;

}

public void setInput(InputStream input) {

this.input = input;

}

public List<Person> getPersons(){

persons = new ArrayList<Person>();

DocumentBuilder builder = null;

Document document = null;

try {

// 通过Dom工厂方法建立Dom解析器

builder =

DocumentBuilderFactory.newInstance().newDocumentBuilder();

document = builder.parse(input);

Element element = document.getDocumentElement();

// 取得节点<person>的节点列表

NodeList personNodes =element.getElementsByTagName("person");

// 节点长度

int length = personNodes.getLength();

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

// 取得<person>的节点元素

Element personElement = (Element)personNodes.item(i);

person = new Person();

// 取得<person id="1">中的id属性值

person.setId(personElement.getAttribute("id"));

// 继续向下,取得子节点列表,如<name><age>等等

NodeList childnodes = personElement.getChildNodes();

int len = childnodes.getLength();

for(int j = 0 ; j < len ; j++){

// 如果子节点是一个元素节点

if(childnodes.item(j).getNodeType() == Node.ELEMENT_NODE){

// 取得节点名称

String nodeName = childnodes.item(j).getNodeName();

// 取得节点值

String nodeValue =

childnodes.item(j).getFirstChild().getNodeValue();

if("name".equals(nodeName)){

person.setName(nodeValue);

}

if("age".equals(nodeName)){

person.setAge(nodeValue);

}

}

}

persons.add(person);

}

return persons;

} catch (Exception e) {

e.printStackTrace();

} finally {

document = null;

builder = null;

}

return null;

}

}

2)使用DomHandler进行解析。


public static List<Person> readXMLByDOM(String filePath) {

try {

FileInputStream fis = new FileInputStream(new File(filePath));

DomHandler domHandler = new DomHandler(fis);

return domHandler.getPersons();

} catch (Exception e) {

e.printStackTrace();

}

return null;

}

以上就是使用DOM方式解析XML文件的方法。

下面我们介绍如何采用SAX的方法对XML文件进行读取。

SAX采用基于事件驱动的处理方式,它将XML文档转换成一系列的事件,由单独的事件处理器来决定如何处理。为了了解如何使用SAXAPI处理XML文档,这里介绍一下SAX所使用的基于事件驱动的处理方式。

基于事件的处理方式主要围绕着事件源以及事件处理器来工作的。一个可以产生事件的对象被称为事件源,而可以对事件产生响应的对象就被叫做事件处理器 。事件源与事件处理对象是通过在事件源中的事件注册方法连接的。当事件源产生事件后,调用事件处理器相应的方法,一个事件获得处理。当在事件源调用事件处理器中特定方法的时候,会传递这个事件标志以及其响应事件的状态信息,这样事件处理器才能够根据事件信息来决定自己的行为。

在SAX接口中,事件源是org.xml.sax包中的XMLReader,它通过parser()方法来开始解析XML文档,并根据文档的内容产生事件。而事件处理器则是org.xml.sax包中的ContentHander、DTDHander、ErrorHandler,以及 EntityResolver这4个接口,它们分别处理事件源在解析XML文档过程中产生的不同种类的事件。而事件源XMLReader与这4个事件处理器的连接是通过在XMLReader中的相应事件处理器注册方法setXXXX()来完成的,详细介绍请见表5-1所示。


处理器名称


处理事件


XMLReader注册方法


ContentHandler


跟文档内容有关的事件

1)文档的开始与结束

2)xml元素的开始与结束

3)可忽略的实体

4)名称空间前缀映射的开始和结束

5)处理指令

6)字符数据和可忽略的空格


setContentHandler(ContentHandler h)


ErrorHandler


处理XML文档时产生的错误


setErrorHandler(ErrorHandler h)


DTDHandler


处理对文档的DTD进行解析时产生的事件


setDTDHandler(DTDHandler h)


EntityResolover


处理外部实体


setEntityResolover(EntityResolover h)

表5-1 事件处理器介绍

以上的4个事件源处理器接口,在开发中没有必要直接从这4个接口直接继承,因为org.xml.sax.helper包为我们提供了类 DefaultHandler,其继承了这4个接口,在实际开发中直接从DefaultHandler继承并实现相关方法就可以了。

在这4个接口中,最重要的是ContentHanlder接口,下面就其中的方法加以说明,如下:

1)startDocument()

当遇到文档开始的时候,调用这个方法,可以在其中做一些预处理的工作。

2)endDocument()

与遇到文档开始的方法相对应,当文档结束的时候,调用这个方法,可以在其中做一些善后的工作。

3) startElement(String namespaceURI, String localName, String qName,Attributes atts)

当读到一个开始标签的时候,会触发这个方法。namespaceURI就是命名空间,localName是不带命名空间前缀的标签名,qName是带命名空间前缀的标签名。通过atts可以得到所有的属性名和相应的值。要注意的是SAX中一个重要的特点就是它的流式处理,当遇到一个标签的时候,它并不会纪录下以前所碰到的标签,也就是说,在startElement()方法中,所有你所知道的信息,就是标签的名字和属性,至于标签的嵌套结构,上层标签的名字,是否有子元属等等其它与结构相关的信息,都是不得而知的,都需要我们的程序来完成。这使得SAX在编程处理上没有DOM来得那么方便。

4)endElement(String uri, String localName, String name)

同理,在遇到结束标签的时候,调用这个方法。

5)characters(char[] ch, int start, int length)

这个方法用来处理在XML文件中读到的内容,第一个参数为文件的字符串内容,后面两个参数是读到的字符串在这个数组中的起始位置和长度,使用new String(ch,start,length)就可以获取内容。

只要为SAX提供实现ContentHandler接口的类,那么该类就可以得到通知事件(实际上就是SAX调用了该类中的回调方法)。因为ContentHandler是一个接口,在使用的时候可能会有些不方便,因此,SAX还为其制定了一个Helper类:DefaultHandler,它实现了ContentHandler接口,但是其所有的方法体都为空,在实现的时候,你只需要继承这个类,然后重载相应的方法即可。

使用SAX解析android.xml的代码如下:

1)实现DefaultHandler接口。


// import略

public class SaxHandler extends DefaultHandler {

private List<Person> persons;

private Person person;

// tagName的作用是记录解析时的上一个节点名称

private String tagName;

public List<Person> getPersons(){

return persons;

}

/**

* 节点处理

*/

@Override

public void characters(char[] ch, int start, int length) throws SAXException

{

String data = new String(ch, start, length);

if("name".equals(tagName)){

person.setName(data);

}

if("age".equals(tagName)){

person.setAge(data);

}

}

/**

* 元素结束

*/

@Override

public void endElement(String uri, String localName, String qName)

throws SAXException {

if("person".equals(localName)){

persons.add(person);

person = null;

}

tagName = null;

}

/**

* 文档开始

*/

@Override

public void startDocument() throws SAXException {

persons = new ArrayList<Person>();

}

/**

* 元素开始

*/

@Override

public void startElement(String uri, String localName, String qName,

Attributes attributes) throws SAXException {

if("person".equals(localName)){

person = new Person();

person.setId(attributes.getValue("id"));

}

// 将正在解析的节点名称赋给tagName

tagName = localName;

}

}

2)使用SaxHandler进行解析。


public static List<Person> readXMLBySAX(String file_path) {

try {

FileInputStream fis = new FileInputStream(new File(file_path));

SAXParser parser = SAXParserFactory.newInstance().newSAXParser();

SaxHandler saxHandler = new SaxHandler();

parser.parse(fis, saxHandler);

return saxHandler.getPersons();

} catch (Exception e) {

e.printStackTrace();

}

return null;

}

下面继续介绍如何使用Pull方式实现XML文件的解析。

Pull解析器是一个开源的Java项目,Android系统内部解析XML文件均为此种方式。Pull 解析器的运行方式与SAX解析器相似,它提供了类似的事件(开始元素和结束元素),但我们需要使用parser.next()提取它们。事件将作为数值代码被发送,因此可以使用一个简单case-switch语句来实现。

1)实现解析类。


// import略

public class PullHandler {

private InputStream input;

private List<Person> persons;

private Person person;

public PullHandler() {

}

public void setInput(InputStream input) {

this.input = input;

}

public PullHandler(InputStream input) {

this.input = input;

}

public List<Person> getPersons() {

try {

XmlPullParser parser = Xml.newPullParser();

parser.setInput(input, "UTF-8");

int eventType = parser.getEventType();

while (eventType != XmlPullParser.END_DOCUMENT) {

switch (eventType) {

case XmlPullParser.START_DOCUMENT:

// 表示开始文档事件

persons = new ArrayList<Person>();

break;

case XmlPullParser.START_TAG:

// 开始标签

// parser.getName()获取节点的名称

String tag = parser.getName();

if ("person".equals(tag)) {

person = new Person();

// 取得第一个属性值

String id = parser.getAttributeValue(0);

person.setId(id);

}

if (null != person) {

if ("name".equals(tag)) {

// 获取下一个text类型的节点

person.setName(parser.nextText());

}

if ("age".equals(tag)) {

person.setAge(parser.nextText());

}

}

break;

case XmlPullParser.END_TAG:

// XmlPullParser.END_TAG:结束标签

if ("person".equals(parser.getName())) {

persons.add(person);

person = null;

}

break;

}

// 继续下一个元素

eventType = parser.next();

}

input.close();

return persons;

} catch (Exception e) {

e.printStackTrace();

}

return null;

}

}

2)使用PullHandler进行解析。


public static List<Person> readXMLByPULL(String file_path) {

try {

FileInputStream fis = new FileInputStream(new File(file_path));

PullHandler pullHandler = new PullHandler(fis);

return pullHandler.getPersons();

} catch (Exception e) {

e.printStackTrace();

}

return null;

}


经验分享:

如何在实际项目中选用DOM、SAX还是PULL方式,要根据具体的项目情况来决定。以下介绍下三种方式各自的优缺点及使用场合,开发者可以根据项目的具体情况做判断。

DOM(文档对象模型),为XML文档的解析定义了一组接口,解析器读入整个文档,然后构造一个驻留内存的树结构,然后代码就可以使用DOM接口来操组整个树结构,具体分析如下:

优点:整个文档树都在内存当中,便于操作;支持删除、修改、重新排列等多功能。

缺点:将整个文档调入内存(经常包含大量无用的节点),浪费时间和空间。

使用场合:一旦解析了文档还需要多次访问这些数据,而且资源比较充足(如内存、CPU等)。

为了解决DOM解析XML引起的这些问题,出现了SAX。

SAX解析XML文档为事件驱动,详细说明请阅读Android读写XML(中)–SAX 。当解析器发现元素开始、元素结束,文本、文档的开始或者结束时,发送事件,在程序中编写响应这些事件的代码,其特点如下:

优点:不用事先调入整个文档,占用资源少。尤其在嵌入式环境中,极力推荐采用SAX进行解析XML文档。

缺点:不像DOM一样将文档长期驻留在内存,数据不是持久的,事件过后,如没有保存数据,那么数据就会丢失。

使用场合:机器性能有限,尤其是在嵌入式环境,如Android,极力推荐采用SAX进行解析XML文档。

大多数时间,使用SAX是比较安全的,并且Android提供了一种传统的SAX使用方法,以及一个便捷的SAX包装器。如果XML文档比较小,那么DOM可能是一种比较简单的方法。如果XML文档比较大,但只需要文档的一部分,则XML Pull解析器可能是更为有效的方法。最后对于编写XML,Pull解析器包也提供了一种便捷的方法。

时间: 2024-10-11 19:17:47

第五章 以数据为中心—数据存取(2)的相关文章

第五章 以数据为中心—数据存取(1)

第五章以数据为中心-数据存取 典型的桌面操作系统提供一种公共文件系统--任何应用软件可以使用它来存储和读取文件,该文件也可以被其它的应用软件所读取(会有一些权限控制设定).而Android 采用了一种不同的系统.在Android 中,所有的应用软件数据(包括文件)为该应用软件所私有.然而,Android 同样也提供了一种标准方式供应用软件将私有数据开放给其它应用软件.这一章将描述一个应用软件存储和获取数据.开放数据给其他应用软件.从其他应用软件请求数据并且开放它们的多种方式. 可供选择的存储方式

第五章 以数据为中心—数据存取(5)

5.2.3添加.删除和修改操作 下面具体说明如何进行添加.删除.修改的操作.下面我们将这些动作封装在一个类DBHelper中,通过这个类的几个方法,可以具体看到如何进行数据库的各种操作. // import略 public class DBHelper { private static final String[] COLS = new String[] { "_id","name"}; private SQLiteDatabase db; private fina

第五章 以数据为中心—数据存取(6)

5.3安全方便简单-使用SharedPreferences 前面操作文件和数据库都相对比较复杂,需要打开,读取,关闭等等操作.可能有人就想,如果我只是需要存取几个简单的数据,有没有简单点的方法呢?的确,在Android中也封装了一种轻便的数据存取的方法-Preferences. Preferences是一种轻量级的数据存储机制,它将一些简单数据类型的数据,包括boolean类型.int类型.float类型.long类型以及String类型的数据,以键值对的形式存储在应用程序的私有Preferen

第五章 以数据为中心—数据存取(3)

5.1.3自由操作随心所欲-序列化和反序列化 在Android开发中,除了经常会操作普通的文本文件和XML文件以外,也会经常使用序列化和反序列化的方式传递或者存取数据. Android序列化对象主要有两种方法,实现Serializable接口.或者实现Parcelable接口.实现Serializable接口是Java SE本身就支持的,而Parcelable是Android特有的功能,效率比实现Serializable接口高,而且还可以用在IPC中.实现Serializable接口非常简单,声

第五章 以数据为中心—数据存取(4)

5.2通用的数据操作方式-数据库 说到数据的存取,数据库肯定是一个常用的解决方案.Android中也有它自己的数据库,下面我们来看看Android中的数据库与一般的数据库有什么不一样的地方. 5.2.1SQLite数据库介绍 目前在Android系统中集成的是SQLite3版本,它支持SQL语句,它是一个轻量级的嵌入式数据库.SQLite支持NULL.INTEGER.REAL.TEXT和BLOB数据类型,不支持静态数据类型,而是使用列关系.可以把SQLite数据库近似看成是一种无数据类型的数据库

第七章 以数据为中心的程序设计

本章介绍 ■ 表示和处理文档 ■ 设计不可变数据结构 ■ 数据表示形式之间的转换 ■ 使用记录和递归差别联合 在设计函数式程序时,首先要考虑的是程序处理的数据:由于重要的程序都使用数据,因此,在应用程序设计中,这个方面极为重要.用函数语言实现程序,我们也是从设计在代码中用到的数据结构开始:下一步才会写处理数据的运算. 但是不同于面向对象的设计,它是把数据封装在对象的状态中:处理用方法表示,是对象的一部分,在运算中还可以和其他有关对象进行交互.而大多数函数式程序是以数据为中心的,因此,数据与运算明

第五章 数据存储

第五章 数据存储 我们用解析器解析出数据之后,接下来的一步就是对数据进行存储了,保存的形式可以多种多样,最简单的形式可以直接保存为文本文件,如 TXT.Json.CSV 等等,另外还可以保存到数据库中,如关系型数据库 MySQL,非关系型数据库 MongoDB.Redis 等等.那么本章我们就来统一了解一下数据的保存方式. 5.1 文件存储 文件存储形式可以是多种多样的,比如可以保存成 TXT 纯文本形式,也可以保存为 Json 格式.CSV 格式等,本节我们来了解下文本文件的存储方式. 5.1

第五章:大数据 の HBase 进阶

本课主题 HBase 读写数据的流程 HBase 性能优化和最住实践 HBase 管理和集群操作 HBase 备份和复制 引言 前一篇 HBase 基础 (HBase 基础) 简单介绍了NoSQL是什么和重点讨论 HBase 数据库的特点,在这一篇会进一步介绍 HBase 的其他特性和机制,比如读写数据的流程,在读写过程中 WAL,MemStore 的作用,还会谈谈一些数据缓存机制,明白在什么场景下需要用那一种缓存策略?如何对频繁使用的数据进行快速响应,从检索上提升查询效率和从内存层面上运用不同

大数据日知录【第五章:分布式协调系统】笔记--如何当选老大?如何加入组织?如何将组织的意愿传递下去

Chubby: 保持高可用性和可靠性,不追求读写的高吞吐量,所有的读写请求都有主控服务器完成,其他的备份服务器在内存中维护和主控服务器完全一致的树形结构,此时从属服务器的作用在于系统的整体的可靠性. Zookeeper: 每一个节点都可以处理读请求,写请求只能由主控服务器处理,此时从属服务器的作用在于提高系统读的吞吐量(有可能会导致读取的数据是老数据). Zookeeper主要应用场景: 选举老大 配置管理:配置文件存储在某个节点上,其他节点都是观察者,启东市可以读取也可以订阅改配置消息. 组成