xml文件在各种开发中都广泛应用。Android中也不例外。作为承载数据的一个重要角色,如何读写xml成为一项十分重要的技能。
在Android中,常见的xml解析器分为DOM解析器,SAX解析器,PULL解析器。
将该xml文件放置在assets目录中。
<?xml version="1.0" encoding="utf-8"?>
<persons>
<person id="1">
<name>zhangsan</name>
<age>21</age>
</person>
<person id="2">
<name>lisi</name>
<age>22</age>
</person>
<person id="3">
<name>wangwu</name>
<age>23</age>
</person>
</persons>
实体类
data class Person(val id: Int, val name: String, val age: Int)
DOM 解析器
DOM是基于树形结构的节点和信息片段的集合,允许开发人员使用DOM API遍历xml树,检索所需结构。分析该结构通常需要加载整个文档和构造整个树形结构,然后才可检索和更新节点信息。
在DOM解析过程中,会先把整个xml读入内存中,然后构建一个驻留内存的树结构,这样就可以通过代码使用DOM接口来操作整个树结构。由于DOM在内存中以树形结构存放,因此,检索和更新的效率会更高。但是对于特别大的文档,解析和加载会消耗比较大的资源,如果xml文件比较小,使用DOM也是可行的。
DOM解析的步骤
1.首先利用DocumentBuilderFactory 创建一个DocumentBuilderFactory实例
2.利用DocumentBuilderFactory 创建一个DocumentBuilder实例
3.加载整个xml文档(Document)
4.获取文档的根节点(Element)
5.获取根节点中所有子节点列表(NodeList)
6.然后获取子节点列表中的需要读取的节点
private fun dom(context: Context, fileName: String): ArrayList<Person> {
val persons = ArrayList<Person>()
var inputStream: InputStream? = null
try {
inputStream = context.assets.open(fileName)
val factory = DocumentBuilderFactory.newInstance()
val builder = factory.newDocumentBuilder()
val document = builder.parse(inputStream)
val element = document.documentElement
val items = element.getElementsByTagName("person")
for (i in 0 until items.length) {
val personNode = items.item(i) as Element
val id = personNode.getAttribute("id").toInt()
val name = personNode.getElementsByTagName("name")
.item(0).firstChild.nodeValue
val age = personNode.getElementsByTagName("age")
.item(0).firstChild.nodeValue.toInt()
persons.add(Person(id, name, age))
}
} catch (e: IOException) {
e.printStackTrace()
} catch (e: ParserConfigurationException) {
e.printStackTrace()
} finally {
try {
inputStream?.close()
} catch (e: IOException) {
e.printStackTrace()
}
}
return persons
}
SAX 解析器
SAX是一种基于事件的解析器,事件驱动的解析流程方式是从文档的开始解析到文档的结束。不可暂停或倒退。SAX解析器占用内存少,解析速度快。SAX解析的工作原理是对文档进行顺序扫描,当扫描到文档的开始(Document),元素(Element)的开始与结束,文档(Document)的结束。等地方时通知事件处理函数,由事件处理函数作相关的动作。然后继续扫描,直到文档的结束。
SAX解析的步骤
1.首先利用SAXParserFactory创建一个SAXParserFactory实例
2.利用SAXParserFactory.newSAXParser()返回一个SAXParser解析器
3.利用SAXParser获取一个事件源对象XMLReader
4.实例化一个DefaultHandler对象
5.连接事件源对象XMLReader到事件处理类DefaultHandler中
6.调用XMLReader的parse方法从输入源中获取xml数据
7.通过DefaultHandler返回我们所需的数据
private fun sax(context: Context, fileName: String): ArrayList<Person> {
val persons = ArrayList<Person>()
var inputStream: InputStream? = null
try {
inputStream = context.assets.open(fileName)
val factory = SAXParserFactory.newInstance()
val parser = factory.newSAXParser()
val reader = parser.xmlReader
val handler = XMLContentHandler(persons)
reader.contentHandler = handler
reader.parse(InputSource(inputStream))
} catch (e: IOException) {
e.printStackTrace()
} catch (e: SAXException) {
e.printStackTrace()
} finally {
try {
inputStream?.close()
} catch (e: IOException) {
e.printStackTrace()
}
}
return persons
}
class XMLContentHandler(private val persons: ArrayList<Person>) : DefaultHandler() {
private var id = 0
private var name = ""
private var age = 0
private var tagName: String? = null
//文档开始时调用
override fun startDocument() {
super.startDocument()
}
//读取到一个标签开始时调用
override fun startElement(uri: String, localName: String, qName: String, attributes: Attributes) {
super.startElement(uri, localName, qName, attributes)
if ("person" == localName) {
id = attributes.getValue("id").toInt()
}
tagName = localName
}
//读取到节点内容时调用
override fun characters(ch: CharArray, start: Int, length: Int) {
super.characters(ch, start, length)
if (tagName != null) {
val data = String(ch, start, length)
if ("name" == tagName) {
name = data
} else if ("age" == tagName) {
age = data.toInt()
}
}
}
//读取到一个标签结束时调用
override fun endElement(uri: String, localName: String, qName: String) {
super.endElement(uri, localName, qName)
if ("person" == localName) {
persons.add(Person(id, name, age))
}
tagName = null
}
//文档结束时调用
override fun endDocument() {
super.endDocument()
}
}
PULL 解析器
PULL解析器的运行方式与SAX类似,它提供了相似的事件,如开始元素和结束元素事件。不同的是,PULL解析在解析过程中返回的是数字。且需要我们自己获取产生的事件,然后做相应的处理。而不像SAX由处理器触发一些事件。
PULL解析器小巧轻便,解析速度快,简单易用,非常适合在Android设备中使用。Android系统内部在解析xml时也是使用的PULL解析器。Android官方推荐开发者使用PULL解析器。
PULL解析的步骤
1 创建一个XmlPullParser 对象
2 添加文档信息进XmlPullParser解析器
3 得到节点信息
private fun pull(context: Context, fileName: String): ArrayList<Person> {
val persons = ArrayList<Person>()
var inputStream: InputStream? = null
var id = 0
var name = ""
var age = 0
try {
inputStream = context.assets.open(fileName)
val parser = Xml.newPullParser()
parser.setInput(inputStream, "UTF-8")
var evtType = parser.eventType
while (evtType != XmlPullParser.END_DOCUMENT) {
when (evtType) {
XmlPullParser.START_DOCUMENT -> {
//文档的开始
}
XmlPullParser.START_TAG -> {
//开始标签
when (parser.name) {
"person" -> id = parser.getAttributeValue(null, "id").toInt()
"name" -> name = parser.nextText()
"age" -> age = parser.nextText().toInt()
}
}
XmlPullParser.END_TAG -> {
//结束标签
if ("person" == parser.name) {
persons.add(Person(id, name, age))
}
}
}
evtType = parser.next()
}
} catch (e: IOException) {
e.printStackTrace()
} finally {
try {
inputStream?.close()
} catch (e: IOException) {
e.printStackTrace()
}
}
return persons
}
以上三种解析xml文件的结果是一样的。