通常情况下,每个需要访问网络的应用程序都会有一个自己的服务器,我们可以向服务器提交数据,也可以从服务器上获取数据。网络上传输数据时最常用的格式有两种:XML 和 JSON。
这里以之前的一篇文章 Android网络访问之HttpURLConnection和HttpClient 为基础,继续添加代码,介绍 XML 和 JSON 的解析方式。
0. 搭建 Web 服务器
这里首先需要搭建了一个简单的 Web 服务器,在这个服务器上提供一段 XML 格式文本和一段 JSON 格式文本,然后在程序里访问这个服务器,分别对其进行解析。
关于如何下载安装 Apache 服务器,这里有篇文章写得很详细: 如何从Apache官网下载windows版apache服务器
0.1 添加 XML 文件
安装好之后,在其文件夹下的 htdocs 文件夹中新建一个名为 get_data.xml 的文件,添加如下代码:
<apps>
<app>
<id>1</id>
<name>Google Maps</name>
<version>1.0</version>
</app>
<app>
<id>2</id>
<name>Chrome</name>
<version>2.1</version>
</app>
<app>
<id>3</id>
<name>Google Play</name>
<version>2.3</version>
</app>
</apps>
此时在浏览器中访问 http://127.0.0.1/get_data.xml 这个网址,显示如下:
0.2 添加 JSON 文件
在 htdocs 文件夹中新建一个名为 get_data.json 的文件,添加如下代码:
[{"id":"5", "version":"5.5", "name":"Angry Brid"},
{"id":"6", "version":"7.0", "name":"Super Mario"},
{"id":"7", "version":"9.0", "name":"Plants vs. Zombies"}]
在浏览器访问 http://127.0.0.1/get_data.json,显示如下:
注意需要开启相应服务。
1. 解析 XML 格式数据
解析 XML 格式数据方法有很多种,这里介绍常用的两种:Pull 方式和 SAX 方式(此外 DOM 解析方式也比较常用)。
修改MainActivity 中 sendRequestWithHttpClient() 方法中的代码,将 http 请求网址改为 http://10.0.2.2/get_data.xml (PS: 10.0.2.2
对模拟器来说就是电脑本机 IP 地址)。
1.1 Pull
创建 parseXMLWithPull() 方法,并在 sendRequestWithHttpClient() 方法中调用它即可,且无需再发送 Message。代码如下:
/**
* 解析 XML 格式数据(Pull 解析方式)
*/
private void parseXMLWithPull(String xmlData) {
try {
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();// 获取 XmlPullParserFactory实例
XmlPullParser xmlPullParser = factory.newPullParser();// 得到 XmlPullParser 的对象
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) {
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;
}
case XmlPullParser.END_TAG: {// 完成解析某个节点
if ("app".equals(nodeName)) {
Log.e("MainActivity", "id is " + id);
Log.e("MainActivity", "name is " + name);
Log.e("MainActivity", "version is " + version);
}
break;
}
default:
break;
}
eventType = xmlPullParser.next();
}
} catch (XmlPullParserException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
运行程序,LogCat 中显示如下:
说明已经将 XML 文件中的内容成功解析出来了。
1.2 SAX
SAX 解析较 Pull 方式略复杂(通常会新建一个类继承自 DefaultHandler,并重写父类的5个方法),但在语义方面更清晰。
新建一个类 ContentHandler 继承自 DefaultHandler,代码如下:
/**
* 解析 XML 格式数据(SAX 解析方式)
*/
public class ContentHandler extends DefaultHandler {
private String nodeName;
private StringBuilder id;
private StringBuilder name;
private StringBuilder version;
@Override
public void startDocument() throws SAXException {// 开始解析XML时调用
id = new StringBuilder();
name = new StringBuilder();
version = new StringBuilder();
}
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
// 开始解析某个结点时调用
// 记录当前结点名
nodeName = localName;
}
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
// 获取结点内容时调用。
// 在获取结点内容时,本方法可能被调用多次,一些换行符也被当做内容解析出来,需要做好控制(如本例中的trim方法)
// 根据当前的结点名判断将内容添加到哪一个StringBuilder中
if ("id".equals(nodeName)) {
id.append(ch, start, length);
} else if ("name".equals(nodeName)) {
name.append(ch, start, length);
} else if ("version".equals(nodeName)) {
version.append(ch, start, length);
}
}
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
// 结束解析某个结点时调用
if ("app".equals(localName)) {
Log.e("ContentHandler", "id is " + id.toString().trim());
Log.e("ContentHandler", "name is " + name.toString().trim());
Log.e("ContentHandler", "version is " + version.toString().trim());
// 清空StringBuilder
id.setLength(0);
name.setLength(0);
version.setLength(0);
}
}
@Override
public void endDocument() throws SAXException {
// 完成解析XML时调用
}
}
接下来在 MainActivity 中添加 parseXMLWithSAX() 方法,并在 sendRequestWithHttpClient() 方法中调用它即可,代码如下:
/**
* 解析 XML 格式数据(SAX 解析方式)
*/
private void parseXMLWithSAX(String xmlData) {
SAXParserFactory factory = SAXParserFactory.newInstance();
try {
XMLReader xmlReader = factory.newSAXParser().getXMLReader();
ContentHandler handler = new ContentHandler();
xmlReader.setContentHandler(handler);// 将ContentHandler的实例设置到XMLReader中
xmlReader.parse(new InputSource(new StringReader(xmlData)));// 开始执行解析
} catch (Exception e) {
e.printStackTrace();
}
}
运行程序,效果同上。
2. 解析 JSON 格式数据
JSON 与 XML 相比的优缺点:
- 优点:体积小,省流量。
- 缺点:语义性较差。
解析 JSON 格式的数据也有多种方法,这里介绍两种:JSONObject 和 GSON。
2.1 JSONObject
在 MainActivity 中创建 parseJSONWithJSONObject() 方法,并在 sendRequestWithHttpClient() 方法中调用它即可,同样无需发送 Message。代码如下:
/**
* 解析 JSON 格式的数据(JSONObject 解析方式)
*/
private void parseJSONWithJSONObject(String jsonData) {
try {
JSONArray jsonArray = new JSONArray(jsonData);// 将服务器返回的数据传入 JSONArray 对象中
for (int i = 0; i < jsonArray.length(); i++) {
JSONObject jsonObject = jsonArray.getJSONObject(i);
String id = jsonObject.getString("id");
String name = jsonObject.getString("name");
String version = jsonObject.getString("version");
Log.e("MainActivity", "id is " + id);
Log.e("MainActivity", "name is " + name);
Log.e("MainActivity", "version is " + version);
}
} catch (JSONException e) {
e.printStackTrace();
}
}
注意要将 http 请求网址改为 http://10.0.2.2/get_data.json
可以看到这种方式代码更简单。运行程序,LogCat 中显示如下:
2.2 GSON
GSON 并未添加到 Android 的官方 API 中,因此若想使用的话需要在项目中添加 GSON 的 jar 包(下载 后将其添加到 libs 文件夹下即可)。
新建一个 APP 类,代码如下:
public class App {
private String id;
private String name;
private String version;
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 getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
}
在 MainActivity 中创建 parseJSONWithGSON() 方法,并在 sendRequestWithHttpClient() 方法中调用它即可,同样无需发送 Message。代码如下:
/**
* 解析 JSON 格式的数据(GSON 解析方式)
*/
private void parseJSONWithGSON(String jsonData) {
Gson gson = new Gson();
// 借助TypeToken将期望解析成的数据类型传入到fromJson()方法中
List<App> appList = gson.fromJson(jsonData, new TypeToken<List<App>>() {}.getType());
for (App app : appList) {
Log.e("MainActivity", "id is " + app.getId());
Log.e("MainActivity", "name is " + app.getName());
Log.e("MainActivity", "version is " + app.getVersion());
}
}
运行程序,结果同上。