我所学到的任何有价值的知识都是由自学中得来的。—— 达尔文
小弟初学安卓,该文算是小弟的学习过程,课后笔记与一些自己的思考,希望在自己的自学路上留下印记,也许有一些自己想的不对的地方,希望各位前辈斧正。
在网络中,数据的传输格式并不是随心所欲的,当我们想向服务器端传输数据或是从服务器端获取数据,这些数据都必须是格式化后的数据。这些数据有一定的结构规格和语义,任何一个接收数据方都必须对其进行解析才能获得他们想要得到的数据。
网络传输的数据有两种常见的格式:XML(可扩展标记语言)和JSON(JavaScript Object Notation,是一种轻量级的数据交换格式)
如何解析XML
首先我写了一个简易的XML文件:
<?xml version="1.0" encoding="UTF-8" ?>
<web>
<item id="0" url="http://www.baidu.com">百度</item>
<item id="1" url="http://www.taobao.com">淘宝</item>
</web>
1.SAX解析
SAX解析方法是一种高效的解析方法,它逐行扫描文档,边扫描边解析两不误。
首先要新建一个类来继承DefaultHandler类,我重写了:
- startDocument()
- endDocument()
- startElement(String uri, String localName, String qName, Attributes attributes)
- characters(char[] ch, int start, int length)
- endElement(String uri, String localName, String qName)
startDocument()就是整个文档(XML)解析前执行的方法,而endDocument()相反为解析后执行的方法。
startElement就开始动真格的了,Element意为元素,元素应该就如上面XML文件中的<web>内容</web>、<item...>内容</item>,这个方法就是各个元素进行解析,参数中uri是资源标识符,而这个localName是这个元素的元素名(元素又和标签不同,元素是由开始标签与结束标签组成并包含一些内容(content)的,单独的<web>、</web>才叫标签)。第三个参数qName暂时没用到,它应该是带前缀的localName即 “前缀:+localName” ,才疏学浅说不清楚所以推荐一下别人博客的讲解:[QName]。而attributes就是属性,如上面代码中,开始标签ITEM里的“id”、“url”就是其属性。(注意startElement是会多次执行的,每条元素都会执行一次)
characters方法则是在解析元素所含内容时执行,比如上面代码中的“百度”、“淘宝”,第一个参数的字符数组,这个数组大家可以自己遍历一下它并不单单是元素内容的数组,所以第二个参数start才是内容在数组中开始的位置,而length不是数组的长度而是这段元素内容的长度。
endElement是解析结束后执行。
以下是自定义的WebUrl类 和 SAXParseHandler类(extends DefaultHandler )
**WebUrl类 **
package com.qdf.geekband_sixth_week.XML;
public class WebURL {
private int mID;
private String mUrl;
private String mContent;
public int getID() {
return mID;
}
public void setID(int ID) {
mID = ID;
}
public String getUrl() {
return mUrl;
}
public void setUrl(String url) {
mUrl = url;
}
public String getContent() {
return mContent;
}
public void setContent(String content) {
mContent = content;
}
}
SAXParseHandler类
package com.qdf.geekband_sixth_week.XML;
import android.text.TextUtils;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import java.util.ArrayList;
import java.util.List;
public class SAXParseHandler extends DefaultHandler {
List<WebURL> mWebURLs;
WebURL mWebURL;
boolean state = false;
@Override
public void startDocument() throws SAXException {
//开始解析整个文档前
super.startDocument();
mWebURLs = new ArrayList<>();
}
@Override
public void endDocument() throws SAXException {
//文档解析后
super.endDocument();
}
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
//开始解析元素
super.startElement(uri, localName, qName, attributes);
mWebURL = new WebURL();
if (TextUtils.equals(localName, "item")) {
for (int i = 0; i < attributes.getLength(); i++) {
if (TextUtils.equals(attributes.getLocalName(i), "id")) {
//attributes.getLocalName(i)是获取属性名
mWebURL.setID(Integer.parseInt(attributes.getValue(i).trim()));
} else if (TextUtils.equals(attributes.getLocalName(i), "url")) {
mWebURL.setUrl(attributes.getValue(i).trim());
}
}
state = true;
}
}
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
//元素解析后
super.endElement(uri, localName, qName);
if (TextUtils.equals(localName, "item")) {
mWebURLs.add(mWebURL);
}
}
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
//characters()方法会在获取结点中内容的时候调用
super.characters(ch, start, length);
String content = new String(ch, start, length);
if (state) {
mWebURL.setContent(content);
state = false;
}
}
public List<WebURL> getXMLList() {
return mWebURLs;
}
}
首先,这个WebUrl里只有三个私有变量和它们的get和set方法,看变量名大家可以知道,我要将XML文件解析元素获得的属性id、url和内容content放入WebUrl的对象,一条元素做一个对象。
而在SAXParseHandler类中startElement方法,我先判断元素名,若元素名为“item”则继续遍历属性,attributes.getLength()为元素属性的数量,attributes.getLocalName()则是获得属性名,判断属性名并获得属性值的值并add()到我们的WebURL对象中。
characters方法中,我则是直接将元素包含的内容添加进WebURL对象,设置了一个布尔值state用来判断当时解析的元素名是不是指定的元素名,是的话为true才添加内容至WebURL对象。
最后endElement方法中,我们将WebURL对象添加到List<WebURL> mWebURLs集合中。我们这有两个元素,所以集合中有两个WebURL对象。这里还提供了个公共方法getXMLList()来让别的类获取到这个集合。
现在我们就来实验检测结果吧,我将这个本地xml文件方法了资源文件夹的raw文件夹中,然后我们在一个按钮的点击监听事件中解析xml吧。
代码如下:
SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
SAXParser saxParser = saxParserFactory.newSAXParser();
XMLReader xmlReader = saxParser.getXMLReader();//获取XMLReader实例
SAXParseHandler saxParseHandler = new SAXParseHandler();
xmlReader.setContentHandler(saxParseHandler);
InputStream inputStream = getResources().openRawResource(R.raw.test);
InputSource inputSource = new InputSource(inputStream);
xmlReader.parse(inputSource);
saxParseHandler.getXMLList();
可以看到我们解析XML的主角是XMLReader,而我们要将我们的SAXParseHandler处理类对象设置给它,最后才是个完整的自定义解析器。
这里说一下,不管是从本地还是来自网络的数据,我们都要先将其转为输入流的形式来处理,这里可以看到我们将xml文件读入流,并转为InputSource对象(输入源)给XMLReader 解析,这个方法最后获得了集合,而我们通过集合各WebURL对象的get方法来获得我们要的数据,如下Log输出。
2.PULL解析
PULL方法解析相对简单,但必须在线程中执行。
⑴解析本地的xml文件
先获取XmlResourceParser对象并获得了xml资源,通过 getEventType()可以得到当前的解析事件,然后在循环中不断地进行解析(有些类似SAX解析各个元素),如果当前的解析事件不为 XmlResourceParser.END_DOCUMENT,说明解析工作还没完成,调用 next()方法后可以获取下一个解析事件。
new Thread(new Runnable() {
@Override
public void run() {
XmlResourceParser xmlResourceParser = getResources().getXml(R.xml.test);
try {
while (xmlResourceParser.getEventType() != XmlPullParser.END_DOCUMENT) {
//若文档未结束 则一直进行循环
if (xmlResourceParser.getEventType() == XmlPullParser.START_TAG) {
String tagName = xmlResourceParser.getName();
if (TextUtils.equals(tagName, "item")) {
String id = xmlResourceParser.getAttributeValue(0);
String url = xmlResourceParser.getAttributeValue(1);
String content=xmlResourceParser.nextText();
Log.i("Pull", "id: " + id + " url: " + url+" content: "+content);
}
}
xmlResourceParser.next();
}
} catch (XmlPullParserException | IOException e) {
e.printStackTrace();
}
}
}).start();
⑵解析网络的xml(可能说的不对)
解析步奏大致相同,但获取的是XmlPullParser对象如下,而最后xmlPullParser.setInput()方法有两种传参,一种是Reader in,一种是InputStream inputStream (输入流),String inputEncoding(编码)。下面代码中的xmlData为转换为字符串的xml文档。
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
XmlPullParser xmlPullParser = factory.newPullParser();
xmlPullParser.setInput(new StringReader(xmlData));