前言
在android开发中,从服务器拿到的xml数据存在两种解析方式:
- Pull解析方式
- SAX解析方式
接下来我们来一一说一下具体用法。
这里为了测试,我们首先在测试服务器上放上一个get_date.xml文件,以方便我们终端来请求解析。文件内容如下:
1.Pull解析方式
代码如下:
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.send_req:
sendRequestWithHttpClient();
break;
default:
break;
}
}
// The code that request is Success
private static final int REQUEST_SUCCESS = 200;
// Encoding format
private static final String UTF_8 = "UTF-8";
/** * HttpClient * * The HttpClient was supported by Apache * */
private void sendRequestWithHttpClient() {
new Thread(new Runnable() {
@Override
public void run() {
// this is a GET request
try {
HttpClient httpClient = new DefaultHttpClient();
HttpGet httpGet = new HttpGet("http://127.0.0.1/get_data.xml");
HttpResponse httpResponse = httpClient.execute(httpGet);
if (httpResponse.getStatusLine().getStatusCode() == REQUEST_SUCCESS) {
HttpEntity entity = httpResponse.getEntity();
String response = EntityUtils.toString(entity,UTF_8);
// Parse xml data by Pull method
parseXMLWithPull(response);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
/** * parse XML data by Pull method * * @param xmlData */
private void parseXMLWithPull(String xmlData) {
try {
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
XmlPullParser xmlPullParser = factory.newPullParser();
xmlPullParser.setInput(new StringReader(xmlData));
int eventType = xmlPullParser.getEventType();
String id = "";
String name = "";
String version = "";
while (eventType != XmlPullParser.END_DOCUMENT) {
String nodeName = xmlPullParser.getName();
switch (eventType) {
// Begin to parse a tag
case XmlPullParser.START_TAG:
if ("id".equals(nodeName)) {
id = xmlPullParser.nextText();
} else if ("name".equals(nodeName)) {
name = xmlPullParser.nextText();
} else if ("version".equals(nodeName)) {
version = xmlPullParser.nextText();
}
break;
// When we parsed a tag,print the infos
case XmlPullParser.END_TAG:
if ("app".equals(nodeName)) {
Log.d("Info","id is " + id);
Log.d("Info","name is " + name);
Log.d("Info","version is " + version);
}
break;
default:
break;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
这里首先要获取到一个XmlPullParserFactory 的 实 例 , 并 借 助 这 个 实 例 得 到 XmlPullParser 对 象 , 然 后 调 用
XmlPullParser 的 setInput()方法将服务器返回的 XML 数据设置进去就可以开始解析了。解析的过程也是非常简单,通过 getEventType()可以得到当前的解析事件,然后在一个 while 循环中不断地进行解析,如果当前的解析事件不等于 XmlPullParser.END_DOCUMENT,说明解析工作还没完成,调用 next()方法后可以获取下一个解析事件。在 while 循环中,我们通过 getName()方法得到当前结点的名字,如果发现结点名等于id、name 或 version,就调用 nextText()方法来获取结点内具体的内容,每当解析完一个 app结点后就将获取到的内容打印出来。
打印结果:
2.SAX解析方式
除了Pull方式来解析外,我们还可以用SAX方式来解析,用法上比Pull方式稍微复杂一些,但是语义方面更加清晰。
通常情况下我们都会新建一个类继承自 DefaultHandler,并重写父类的五个方法,如下所示:
这里说明一下上面几个方法的用处:
- startDocument() : 开始 XML 解析的时候调用
- startElement() : 开始解析某个结点的时候调用
- characters() : 在获取结点中内容的时候调用
- endElement() : 在完成解析某个结点的时候调用
- endDocument() : 在完成整个 XML 解析的时候调用
其中,startElement()、characters()和 endElement()这三个方法是有参数的,从 XML 中解析出的数据就会以参数的形式传入到这些方法中。需要注意的是,在获取结点中的内容时,characters()方法可能会被调用多次,一些换行符也被当作内容解析出来,我们需要针对这种情况在代码中做好控制。
接下来,我们来看看具体应用代码:
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import android.util.Log;
/** * 解析xml * */
public class ContentHandler extends DefaultHandler {
private String nodeName;
private StringBuilder id;
private StringBuilder name;
private StringBuilder version;
@Override
public void startDocument() throws SAXException {
id = new StringBuilder();
name = new StringBuilder();
version = new StringBuilder();
}
@Override
public void endDocument() throws SAXException {}
@Override
public void startElement(String uri,String localName,String qName,Attributes attributes) throws SAXException {
// 记录当前节点名
nodeName = localName;
}
@Override
public void endElement(String uri,String qName)
throws SAXException {
if ("app".equals(localName)) {
Log.d("ContentHandler","id is " + id.toString().trim());
Log.d("ContentHandler","name is " + name.toString().trim());
Log.d("ContentHandler","version is " + version.toString().trim());
// 最后将StringBuilder清空掉
id.setLength(0);
name.setLength(0);
version.setLength(0);
}
}
@Override
public void characters(char[] ch,int start,int length)
throws SAXException {
// 根据当前的节点名判断将内容添加到哪一个StringBuilder中
if ("id".equals(nodeName)) {
id.append(ch,start,length);
} else if ("name".equals(nodeName)) {
name.append(ch,length);
} else if ("version".equals(nodeName)) {
version.append(ch,length);
}
}
}
我们在startDocument()中初始化相关StringBuilder对象,接下来每当开始解析某个结点的时候,startElement()方法就会得到调用,其中 localName 参数记录着当前结点的名字,这里我们把它记录下来。接着在解析结点中具体内容的时候就会调用 characters()方法,我们会根据当前的结点名进行判断,将解析出的内容添加到哪一个 StringBuilder 对象中。最后在 endElement()方法中进行判断,如果 app 结点已经解析完成,就打印出 id、name 和 version 的内容。需要注意的是,目前 id、name 和 version 中都可能是包括回车或换行符的,因此在打印之前我们还需要调用一下 trim()方法,并且打印完成后还要将 StringBuilder 的内容清空掉,不然的话会影响下一次内容的读取。
/** * HttpClient * * The HttpClient was supported by Apache * */
private void sendRequestWithHttpClient() {
new Thread(new Runnable() {
@Override
public void run() {
// this is a GET request
try {
HttpClient httpClient = new DefaultHttpClient();
HttpGet httpGet = new HttpGet("http://127.0.0.1/get_data.xml");
HttpResponse httpResponse = httpClient.execute(httpGet);
if (httpResponse.getStatusLine().getStatusCode() == REQUEST_SUCCESS) {
HttpEntity entity = httpResponse.getEntity();
String response = EntityUtils.toString(entity,UTF_8);
// Parse xml data by SAX method
parseXMLWithSAX(response);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
/** * pares XML data by SAX method * * @param xmlData */
private void parseXMLWithSAX(String xmlData) {
try {
SAXParserFactory factory = SAXParserFactory.newInstance();
XMLReader reader = factory.newSAXParser().getXMLReader();
ContentHandler handler = new ContentHandler();
// 将contentHandler实例设置到reader中
reader.setContentHandler(handler);
// 开始解析
reader.parse(new InputSource(new StringReader(xmlData)));
} catch (Exception e) {
e.printStackTrace();
}
}
附
除了上述两种解析方式外,还有一种DOM解析,这里后面再补上…