android解析raw下xml,xml解析详解

XML是一种非常流行的用来传输和存储数据的语言,因此在日常工作中会经常用到它,是我们必须要掌握的知识之一。

XML指可扩展标记语言(eXtensible Markup Language),XML是被设计用来传输和存储数据。

xml是一种一种树结构,也就是有开头必有结尾,如果开头为,那么结尾就是,如果是最外层的结构,那么其他的数据就被嵌套在其中,格式如下:

冰与火之歌

乔治马丁

2014

89

其中每个又叫做节点,每个节点里面可以嵌套节点,而每个节点也可以有自己的属性如,其中的id就是属性。

那以上面的book为例,它有多少个节点呢?答案是 9 个节点。可是明明数起来只有5个节点:book、name、author、year、price呀。

那是因为空白处也算作是一个节点,如下图:

28bf42c85d60

节点个数.png

所以这样算起来就有9个节点了(开始与闭合标签为同一个节点)。那么回过头来,XML有有哪些常用的节点类型呢?如下图:

它有三种节点类型,Element、Attr、Text这三种,其中Element表示节点;Attr表示节点中的属性(即上面的book的id);Text代表每个节点的内容类型(空白处也是Text类型)。

XML的只是就介绍到这里,现在我们开始解析XML吧,

xml之DOM解析

Dom解析是java中原生的对xml的解析方法,它的特点是简单易用,缺点是会见解析的内容存到内存中去,比较占用资源。

现在我们使用DOM来解析books.xml,这个xml文件位于安卓项目中的res - raw文件夹中。

同样的,我们在XMLParseAndSet添加使用DOM解析的方法

public static ArrayList parseXMLWithDOM(InputStream inputStream) {

ArrayList booklists = new ArrayList();

// 创建一个DocumentBuilderFactory的对象

DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();

// 创建一个DocumentBuilder的对象

try {

// 创建DocumentBuilder对象

DocumentBuilder db = dbf.newDocumentBuilder();

// 通过DocumentBuilder对象的parser方法加载books.xml文件到当前项目下

Document document = db.parse(inputStream);

// 获取所有book节点的集合

NodeList bookList = document.getElementsByTagName("book");

// 遍历每一个book节点

for (int i = 0; i < bookList.getLength(); i++) {

Book books = new Book();

// 通过 item(i)方法 获取一个book节点,nodelist的索引值从0开始

Node book = bookList.item(i);

// 获取book节点的所有属性集合

NamedNodeMap attrs = book.getAttributes();

// 遍历book的属性id

for (int j = 0; j < attrs.getLength(); j++) {

// 通过item(index)方法获取book节点的某一个属性

Node attr = attrs.item(j);

// 获取属性名id

System.out.print("属性名:" + attr.getNodeName());

// 获取属性值id的数值

System.out.println("--属性值" + attr.getNodeValue());

books.setId(attr.getNodeValue());

}

// 解析book节点的子节点

NodeList childNodes = book.getChildNodes();

// 遍历childNodes获取每个节点的节点名和节点值

System.out.println("第" + (i + 1) + "本书共有" + childNodes.getLength() + "个子节点");

for (int k = 0; k < childNodes.getLength(); k++) {

// 区分出text类型的node以及element类型的node

if (childNodes.item(k).getNodeType() == Node.ELEMENT_NODE) {

// 获取了element类型节点的节点名

String nodeName = childNodes.item(k).getNodeName();

// 获取了element类型节点的节点值

String nodeValue = childNodes.item(k).getFirstChild().getNodeValue();

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

books.setName(nodeValue);

}

// 获得book对象的参数

····

}

}

booklists.add(books);

System.out.println("======================结束遍历第" + (i + 1) + "本书的内容=================");

}

return booklists;

}

·······

return null;

}

在这段代码中,我们把xml中的数据解析出来,然后存到ArrayList集合中,返回到Activity显示到界面中。

解释完这段代码的功能,我们开始详细解析DOM解析xml的步骤吧。

1、首先必须要创建DocumentBuilderFactory即DOM工厂类对象

2、通过工厂类的静态方法获得DocumentBuilder对象

3、将传入的流对象放入DocumentBuilder的parse()方法中,获得Document对象

4、通过Document的getElementsByTagName()方法,直接从标签"book"开始解析xml(解析最外层的bookstore没有任何意义),获得NodeList节点结合。

5、通过对NodeList的for循环不断的得到Node,即每个节点对象,然后通过节点对象获取节点名;

6、若节点Node下仍有子节点,可通过getChildNodes()获得所有的子节点的集合对象。

相信前4步大家都不会有过多的疑问,而第4步开始却会产生疑问。所以让我们回到第4步,代码如下:

// 获取所有book节点的集合

NodeList bookList = document.getElementsByTagName("book");

// 遍历每一个book节点

for (int i = 0; i < bookList.getLength(); i++) {

// 通过 item(i)方法 获取一个book节点,nodelist的索引值从0开始

Node book = bookList.item(i);

// 获取book节点的所有属性集合

NamedNodeMap attrs = book.getAttributes();

// 遍历book的属性id

for (int j = 0; j < attrs.getLength(); j++) {

// 通过item(index)方法获取book节点的某一个属性

Node attr = attrs.item(j);

···

}

}

我们在getElementsByTagName("book")这个方法中获得了整个子节点的集合对象bookList,然后操作就跟普通集合一般,要获得里面的参数就必须使用for循环。

然后我们通过for循环,得到Node对象book,这是我们就可以理解为NodeList是Node的集合,而Node则有两个参数,分别为NodeValue节点值和NodeName节点名。但是但节点类型不为Attr和Text的时候,使用getNodeValue()方法只能获得null。

突然间,我们发现book节点中有Attr属性id,想要获取里面的值怎么办?只需要执行Node的getAttributes()方法就可以获得Attr属性的集合NamedNodeMap(一个节点可拥有多个属性),然后通过for循环获得里面的值。

当然,上面获得节点属性的值的方法是当不知道属性名称的时候使用,如果确定属性名称,并知道要获得哪个属性,可用以下方法:

// Element book = (Element) bookList.item(i);

// //通过getAttribute("id")方法获取属性值

// String attrValue = book.getAttribute("id");

紧接着我们要获得book节点下的子节点,如name、author这些节点的信息怎么办?

Node类有getChildNodes()的方法获得子节点的集合NodeList,然后又是一次for循环,代码如下:

NodeList childNodes = book.getChildNodes();

// 遍历childNodes获取每个节点的节点名和节点值

for (int k = 0; k < childNodes.getLength(); k++) {

// 区分出text类型的node以及element类型的node

if (childNodes.item(k).getNodeType() == Node.ELEMENT_NODE) {

// 获取了element类型节点的节点名name、author等

String nodeName = childNodes.item(k).getNodeName();

····

}

}

这时我们以为已经走到了尽头,可以获取name节点中的值,如《冰与火之歌》这样的书名的时候,去发现直接使用getNodeValue()获得了null!!

上面我们已经说过节点类型不为Attr和Text的时候,使用getNodeValue()方法只能获得null,而name的节点类型刚好是Element。

那么该怎么办呢?只有继续使用getChildNodes()方法获取子节点,然后使用getNodeValue()方法了。

但是请等一等,为什么要使用getChildNodes()获取子节点,为什么name里面的书名《冰与火之歌》不属于它自身,而属于子节点呢?

这里就要说一下xml的特点了,xml中每个节点嵌套的数据都是一个子节点,这其中也包括他们中间的value,如果你使用getNodeName()获得value节点名,就会获得Text元素的默认名#text。

写到了这里,相信大家对DOM解析XML已经有了一个比较直观的认识了。使用DOM解析xml就等于是解析一个循环的集合,只要这个集合中还有这子节点,那么就可以使用for循环继续执行下去!

xml之SAX解析

SAX解析xml相对与dom解析来说稍显复杂一点,因为dom解析只需要不断的使用for循环就可以了,但是SAX解析却需要按照步骤来,从xml的开始即到结尾,而且还需要一个helper类来帮助解析。

SAX与dom相比,它速度更快,更有效,而且一遍扫描一边解析,对内存压力教小。但是SAX解析是逐行解析,也就是从从开头到结尾,次序不能乱,你无法解析到第二行的时候让它解析第五行。

介绍完优缺点之后,我们就来看看如何使用SAX解析吧。

我们仍然使用DOM解析时的xml数据。

首先我们要建一个帮助类SAXParserHandler,它继承DefaultHandler,这个帮助类用于帮助解析XML,并且获得xml中的数据。

要想解析xml并获得数据,需要重写DefaultHandler的五个方法,分别为:

@Override

public void startDocument() throws SAXException {

super.startDocument();

}

@Override

public void endDocument() throws SAXException {

// TODO Auto-generated method stub

super.endDocument();

}

@Override

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

Attributes attributes) throws SAXException {

//调用DefaultHandler类的startElement方法

super.startElement(uri, localName, qName, attributes);

}

@Override

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

throws SAXException {

//调用DefaultHandler类的endElement方法

super.endElement(uri, localName, qName);

}

@Override

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

throws SAXException {

// TODO Auto-generated method stub

super.characters(ch, start, length);

}

这些方法有什么用呢?

startDocument()表示xml文档开始解析,而endDocument()已经结束解析了。如果在这两个方法打标签,就会发现,在解析的时候首先输出startDocument()的表情,然后再输出endDocument(),它们都只会被调用一次,由于它们是标识开始与结束的表情,因此一般不需要理会

startElement()和endElement()两个方法,结合XML中有Element类型,很容易得知它们一个节点的开始和节点的结束时使用的。

同时startElement(String uri, String localName, String qName, Attributes attributes)中的几个参数也需要了解一下

uri:xml的命名空间的uri

localName:如果uri命名空间没有被执行,那么为空

qName:节点的名称

attributes:节点的属性名

characters()方法也是一个重要的方法,它是获取节点中数据的方法,也就是获取冰与火之歌中的《冰与火之歌》书名的方法。

介绍完我们就开始正式解析xml吧。

首先我们要判断当前运行到哪个节点,因此修改startElement(),代码如下:

@Override

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

Attributes attributes) throws SAXException {

//调用DefaultHandler类的startElement方法

super.startElement(uri, localName, qName, attributes);

if (qName.equals("book")) {

bookIndex++;

//不知道book元素下属性的名称以及个数,如何获取属性名以及属性值

int num = attributes.getLength();

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

System.out.print("book元素的第" + (i + 1) + "个属性名是:"

+ attributes.getQName(i));

System.out.println("---属性值是:" + attributes.getValue(i));

if (attributes.getQName(i).equals("id")) {

book.setId(attributes.getValue(i));

}

}

}

else if (!qName.equals("name") && !qName.equals("bookstore")) {

System.out.print("节点名是:" + qName + "---");

}

}

判断运行到那个节点很简单,只需要用qName与节点名比较,是否在同一个地址就行了。

然后获取节点中的数据,如运行到name这个节点,要获取书名该怎么办?这时只要重写characters(),设置一个全局变量就能够获取书名

@Override

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

throws SAXException {

// TODO Auto-generated method stub

super.characters(ch, start, length);

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

if (!value.trim().equals("")) {

System.out.println("节点值是:" + value);

}

}

最后运行到endElement(),可以再进行一次判断,判断当前的结束节点标签是不是我们想要的结束节点标签,然后将characters()方法中获得的value变量设置进去。

我们再来看看这五个方法的运行顺序吧,

startDocument() --》 startElement() --》characters() --》 endElement() --》endDocument()

这样帮助类就写好了,那么如何使用这个SAX解析的帮助类呢?

我们继续在XMLParseAndSet中添加SAX解析的方法,代码如下:

public static ArrayList parseXMLWithSAX(InputStream inputStream) {

//获取一个SAXParserFactory实例

SAXParserFactory factory = SAXParserFactory.newInstance();

// 通过factory获取ȡSAXParser实例

try {

SAXParser parser = factory.newSAXParser();

//创建SAXParserHandler对象

SAXParserHandler handler = new SAXParserHandler();

parser.parse(inputStream, handler);

System.out.println("一共有" + handler.getBookList().size()

+ "本书");

···

return handler.getBookList();

}

···

return null;

}

这样我们从外部传入inputStream流就能够解析到books.xml了。

Android中的pull解析

前面是使用java中自带的解析xml的api,现在我们使用android中提供的xml解析器来解析xml吧。

这次解析的数据并不是books.xml,而是存放在assets目录下的backupsms.xml,它的数据形势如下:

110

来警局做个笔录

2015-08-29

如果不太关心xml中的各个标题,可以用以下方法:

public static ArrayList parseXMLWithPull(Context context) {

ArrayList arrayList = null;

SmsBean smsBean = null;

try {

// 1.通过Xml获取一个XmlPullParser对象

XmlPullParser xpp = Xml.newPullParser();

// 2.设置XmlPullParser对象的参数,需要解析的是哪个xml文件,设置一个文件读取流

// 通过context获取一个资产管理者对象

AssetManager assets = context.getAssets();

// 通过资产管理者对象能获取一个文件读取流

InputStream inputStream = assets.open("backupsms.xml");

xpp.setInput(inputStream, "utf-8");

// 3.获取当前xml行的事件类型

int type = xpp.getEventType();

// 4.判断事件类型是否是文档结束的事件类型

while (type != XmlPullParser.END_DOCUMENT) {

// 5.如果不是,循环遍历解析每一行的数据。解析一行后,获取下一行的事件类型

String currentTagName = xpp.getName();

// 判断当前行的事件类型是开始标签还是结束标签

switch (type) {

case XmlPullParser.START_TAG:

if (currentTagName.equals("Smss")) {

// 如果当前标签是Smss,需要初始化一个集合

arrayList = new ArrayList();

} else if (currentTagName.equals("Sms")) {

smsBean = new SmsBean();

smsBean.id = Integer.valueOf(xpp.getAttributeValue(null, "id"));

} else if (currentTagName.equals("num")) {

smsBean.num = xpp.nextText();

} else if (currentTagName.equals("msg")) {

smsBean.msg = xpp.nextText();

} else if (currentTagName.equals("date")) {

smsBean.date = xpp.nextText();

}

break;

case XmlPullParser.END_TAG:

// 当前结束标签是Sms的话,一条短信数据封装完成, 可以加入list中

if (currentTagName.equals("Sms")) {

arrayList.add(smsBean);

}

break;

default:

break;

}

type = xpp.next();// 获取下一行的事件类型

}

return arrayList;

}

···

return null;

}

当然上面那是解析数据时的代码,精简一下,代码会是这样:

public static void parseXMLWithPull(InputStream inputStream) {

try {

// 1.通过Xml获取一个XmlPullParser对象

XmlPullParser xpp = Xml.newPullParser();

xpp.setInput(inputStream, "utf-8");

// 3.获取当前xml行的事件类型

int type = xpp.getEventType();

// 4.判断事件类型是否是文档结束的事件类型

while (type != XmlPullParser.END_DOCUMENT) {

// 5.如果不是,循环遍历解析每一行的数据。解析一行后,获取下一行的事件类型

String currentTagName = xpp.getName();

// 判断当前行的事件类型是开始标签还是结束标签

switch (type) {

case XmlPullParser.START_TAG:

// 开始标签的处理

···

break;

case XmlPullParser.END_TAG:

// 结束标签的处理

···

break;

default:

break;

}

type = xpp.next();// 获取下一行的事件类型

}

}

}

这是一个精简的模板,可以直接拿去修改使用。

当然,更详细的PULL解析xml的方法在Android开发者官网,这里给出地址:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android解析 XML 文件有多种方法,其中最常用的是使用 Android 提供的 XmlPullParser 类。以下是一个简单的示例代码,演示如何使用 XmlPullParser 解析 XML: ```java try { // 创建 XmlPullParser 实例 XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); XmlPullParser parser = factory.newPullParser(); // 设置要解析XML 数据 InputStream inputStream = getResources().openRawResource(R.raw.data); parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false); parser.setInput(inputStream, null); // 开始解析 XML int eventType = parser.getEventType(); while (eventType != XmlPullParser.END_DOCUMENT) { switch (eventType) { case XmlPullParser.START_TAG: String tagName = parser.getName(); if ("item".equals(tagName)) { // 解析 item 标签内的数据 String value = parser.nextText(); // 处理解析到的数据 Log.d(TAG, "Value: " + value); } break; } eventType = parser.next(); } // 解析完成后关闭输入流 inputStream.close(); } catch (XmlPullParserException | IOException e) { e.printStackTrace(); } ``` 在上面的代码中,我们首先创建了一个 XmlPullParser 实例,并设置要解析XML 数据。然后使用 `parser.getEventType()` 获取当前事件类型,通过不断调用 `parser.next()` 方法来遍历 XML 数据。在遍历过程中,我们可以根据不同的事件类型来执行相应的操作,例如在 `START_TAG` 事件中解析标签内的数据。 请注意,上述代码中的 `R.raw.data` 是一个示例 XML 文件的资源引用,你需要替换为你自己的 XML 数据源。 这只是一个简单的示例,实际应用中可能需要根据 XML 结构的复杂程度进行更详细的处理。你可以根据自己的需求来扩展和调整代码。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值