[转帖]在android中解析xml数据_Android, Python及开发编程讨论区_Weblogic技术|Tuxedo技术|中间件技术|Oracle论坛|JAVA论坛|Linux/Unix技术|hadoop论坛_联动北方技术论坛  
网站首页 | 关于我们 | 服务中心 | 经验交流 | 公司荣誉 | 成功案例 | 合作伙伴 | 联系我们 |
联动北方-国内领先的云技术服务提供商
»  游客             当前位置:  论坛首页 »  自由讨论区 »  Android, Python及开发编程讨论区 »
总帖数
1
每页帖数
101/1页1
返回列表
0
发起投票  发起投票 发新帖子
查看: 3913 | 回复: 0   主题: [转帖]在android中解析xml数据        下一篇 
刘伟
注册用户
等级:少校
经验:938
发帖:82
精华:0
注册:2013-6-24
状态:离线
发送短消息息给刘伟 加好友    发送短消息息给刘伟 发消息
发表于: IP:您无权察看 2013-6-27 10:03:32 | [全部帖] [楼主帖] 楼主

可扩展标记语言XML是一套机器可读的编码文件形式。XML是互联网上流行的用于共享数据的格式。网站经常会更新它们的内容,比如新闻网站,博客,它们通常会提供一个XML格式的数据源,这样方便外部程序来及时同步改变的内容。因此对于网络应用来说,上传和解析XML数据就成了共同的任务。这节课主要讲解怎样解析XML文档以及使用它们的数据。

选择解析器 Choose a Parser



我们推荐XmlPullParser,其在android中对XML的解析是高效且可维护的。从过去来看,android已经拥有该接口的两个实现:

  • KXmlParser,通过XmlPullParserFactory.newPullParser()创建。
  • ExpatPullParser,通过Xml.newPullParser()创建。

任意的选择都是可以的,这节中的例子用的是Xml.newPullParser()创建的ExpatPullParser。

分析数据源 Analyze the Feed



解析数据源的第一步是决定哪些属性字段是你需要的,
解析器将提取你需要的属性字段而忽略其他的。以下显示的片段,是来自例子程序中正在被解析的数据源。每一个指向StackOverflow.com的请求都做为一个entry标签显示在数据源中,并且每一个entry标签都含有几个内嵌标签:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

;


<?xml version"utf-8"?>
<feed xmlns"http://backend.userland.com/creativeCommonsRssModule" ...">
<title type="text">newest questions tagged android - Stack Overflow</title>
...
<entry>
...
</entry>
<entry>
<id>http://stackoverflow.com/q/9439999</id>
<re:rank scheme="http://stackoverflow.com">0</re:rank>
<title type="text">Where is my data file?</title>
<category schemeandroid&sort"android"/>
<category schemeandroid&sort"file"/>
<author>
<name>cliff2310</name>
<uri>http://stackoverflow.com/users/1128925</uri>
</author>
<link rel"http://stackoverflow.com/questions/9439999/where-is-my-data-file" />
<published>2012-02-25T00:30:54Z</published>
<updated>2012-02-25T00:30:54Z</updated>
<summary type="html">
<p>I have an Application that requires a data file...</p>

</summary>
</entry>
<entry>
...
</entry>
...
</feed>

;


这个例子程序将会获取entry以及它的内嵌标签title,link和summary的数据。

实例化解析器 Instantiate the Parser*



下一步就是实例化解析器并且开始解析过程了。在以下的代码片段中,实例化了一个不处理命名空间的解析器,并且将提供的InputStream作为输入参数。在调用nextTag)以及readFeed方法后开始解析过程。这两个方法负责获取并处理应用需要的数据:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

;


public class StackOverflowXmlParser {
       // 我们不需要使用命名空间
       private static final String ns = null;

       public List parse(InputStream in) throws XmlPullParserException, IOException {
             try {
                   XmlPullParser parser = Xml.newPullParser();
                   parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);
                   parser.setInput(in, null);
                   parser.nextTag();
                   return readFeed(parser);
             } finally {
             in.close();
       }
}
...
}

;


读取数据源 Read the Feed



readFeed方法用来实际处理数据源,它将entry标签作为递归处理数据源的一个起点,如果标签不是entry,则会跳过它。一旦整个数据源被递归处理,readFeed方法会返回包含entry类型的List对象(包括内嵌标签),而这些数据都是从数据源获取。之后这个List会被解析器返回。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

;


private List readFeed(XmlPullParser parser) throws XmlPullParserException, IOException {
       List entries = new ArrayList();

       parser.require(XmlPullParser.START_TAG, ns, "feed");
       while (parser.next() != XmlPullParser.END_TAG) {
             if (parser.getEventType() != XmlPullParser.START_TAG) {
                   continue;
             }
             String name = parser.getName();
             // 从寻找entry标签开始
             if (name.equals("entry")) {
                   entries.add(readEntry(parser));
             } else {
             skip(parser);
       }
}
return entries;
}

;


解析XML Parse XML



以下是解析XML数据源的具体步骤:

跟(上面提到的)分析数据源一样,先分析出你应用程序想要的标签。这个例子中就是获取entry标签以及它内嵌的title,link和summary标签的数据。

创建以下方法:

  • 为每一个你想要的标签创建“read”方法。比如:readEntry(),readTitle()等等。
    • 解析器从输入流读取标签,当它遇到名为entry,title,link或者summary时,它会调用相对应标签的方法。不然就跳过标签。

    为获取每个不同类型标签的数据并推进到下一个标签定义方法。比如:

    • 对于title和summary标签,实例化的解析器会调用readText()方法,而这个方法获取这些标签的数据则是调用了 parser.getText()方法。
    • 对于link标签,解析器在获取数据的时候会根据第一次的判断,这个链接是否是应用需要的,然后用parser.getAttributeValue()方法获取链接的值。
    • 对于entry标签,解析器会调用readEntry()方法,这个方法会解析entry标签的内嵌标签,title,link和summary,并返回一个Entry对象。
  • 定义一个用于辅助遍历的方法skip(),更多关于skip的信息,详���(跳过你不需要的标签 Skip Tags You Don't Care About)

以下代码片段展示的是解析器如何解析Entry,title,link,以及summary标签:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79

;


public static class Entry {
       public final String title;
       public final String link;
       public final String summary;

       private Entry(String title, String summary, String link) {
             this.title = title;
             this.summary = summary;
             this.link = link;
       }
}

// 解析entry标签的内容,如果遇到title,summary,或者link标签,则在他们各自的"读"方法中处理. 否则跳过标签.
private Entry readEntry(XmlPullParser parser) throws XmlPullParserException, IOException {
       parser.require(XmlPullParser.START_TAG, ns, "entry");
       String title = null;
       String summary = null;
       String link = null;
       while (parser.next() != XmlPullParser.END_TAG) {
             if (parser.getEventType() != XmlPullParser.START_TAG) {
                   continue;
             }
             String name = parser.getName();
             if (name.equals("title")) {
                   title = readTitle(parser);
             } else if (name.equals("summary")) {
                   summary = readSummary(parser);
             } else if (name.equals("link")) {
                   link = readLink(parser);
             } else {
             skip(parser);
       }
}
return new Entry(title, summary, link);
}

// 处理title标签.
private String readTitle(XmlPullParser parser) throws IOException, XmlPullParserException {
       parser.require(XmlPullParser.START_TAG, ns, "title");
       String title = readText(parser);
       parser.require(XmlPullParser.END_TAG, ns, "title");
       return title;
}

// 处理link标签.
private String readLink(XmlPullParser parser) throws IOException, XmlPullParserException {
       String link = "";
       parser.require(XmlPullParser.START_TAG, ns, "link");
       String tag = parser.getName();
       String relType = parser.getAttributeValue(null, "rel");
       if (tag.equals("link")) {
             if (relType.equals("alternate")){
                   link = parser.getAttributeValue(null, "href");
                   parser.nextTag();
             }
       }
       parser.require(XmlPullParser.END_TAG, ns, "link");
       return link;
}

// 处理summary标签.
private String readSummary(XmlPullParser parser) throws IOException, XmlPullParserException {
       parser.require(XmlPullParser.START_TAG, ns, "summary");
       String summary = readText(parser);
       parser.require(XmlPullParser.END_TAG, ns, "summary");
       return summary;
}

// 为title和summary标签获取他们的文本值.
private String readText(XmlPullParser parser) throws IOException, XmlPullParserException {
       String result = "";
       if (parser.next() == XmlPullParser.TEXT) {
             result = parser.getText();
             parser.nextTag();
       }
       return result;
}
...
}

;


跳过你不需要的标签 Skip Tags You Don't Care About



在上述解析XML的描述中,其中的一个步骤是跳过你不需要的标签,以下是解析器的skip方法:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

;


private void skip(XmlPullParser parser) throws XmlPullParserException, IOException {
       if (parser.getEventType() != XmlPullParser.START_TAG) {
             throw new IllegalStateException();
       }
       int depth = 1;
       while (depth != 0) {
             switch (parser.next()) {
                   case XmlPullParser.END_TAG:
                   depth--;
                   break;
                   case XmlPullParser.START_TAG:
                   depth++;
                   break;
             }
       }
}

;


以下为执行过程:

如果当前事件不是START_TAG就抛出一个异常。

解析START_TAG并依此解析至其他的(START_TAG)事件,包括匹配END_TAG事件

为了确保在对应的END_TAG停止,而不是在原START_TAG后面遇到的第一个标签,它会保存内嵌(标签)深度的轨迹。

因此,如果当前标签元素有内嵌标签,depth变量值就不会为0,直到解析器将原START_TAG与相应的END_TAG之前的事件解析完成。

例如,考虑如何让解析器跳过标签,并且这个标签中还内嵌了以及标签:

  • 第一次通过while循环,解析器在标签之后遇到的第一个标记是标签的START_TAG,这时,depth变量值增为2.
  • 第二次通过while循环,解析器遇到的是END_TAG,也就是标签,这时,depth变量会减至1.
  • 第三次通过while循环,解析器遇到的下一个标记是START_TAG,也就是标签,depth变量增为2.
  • 第四次通过while循环,解析与遇到的是END_TAG.也就是标签,depth变量值减至1.
  • 第五次也是最后一次通过while循环,解析器遇到的是END_TAG,也就是,这时depth变量值减至0,这也说明标签已经成功跳过。

使用XML数据 Consume XML Data



这个应用例子使用AsyncTask获取并解析XML数据源,此操作会脱离UI主线程(也就是所谓的异步),当操作完毕,应用会在主activity中修改UI(NetworkActivity)。
以下代码片段中,loadPage方法主要执行:

初始化一个指向XML数据源的Url字符型变量

如果用户的设置以及网络连接允许,则执行new DownloadXmlTask.execute(url)实例化一个新的DownloadXmlTask对象(AsyncTask子类),并运行其execute方法,
下载并解析数据源,然后返回一个用户UI显示的字符串。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

;


public class NetworkActivity extends Activity {
       public static final String WIFI = "Wi-Fi";
       public static final String ANY = "Any";
       private static final String URL android&sort=newest";

       // 是否是Wi-Fi连接.
       private static boolean wifiConnected = false;
       // 是否是手机移动网络连接.
       private static boolean mobileConnected = false;
       // 显示是否要被刷新.
       public static boolean refreshDisplay = true;
       public static String sPref = null;

       ...

       // 使用AsyncTask从stackoverflow.com下载XML数据源.
       public void loadPage() {

             if((sPref.equals(ANY)) && (wifiConnected || mobileConnected)) {
                   new DownloadXmlTask().execute(URL);
             }
             else if ((sPref.equals(WIFI)) && (wifiConnected)) {
                   new DownloadXmlTask().execute(URL);
             } else {
             // 显示错误
       }
}

;


AsyncTask的子类(如下)DownloadXmlTask类实现了AsyncTask的以下方法:

doInBackground(),接收数据源的Url作为参数传入,并调用loadXmlFormNetwork方法,该方法获取并处理数据源,当处理完毕则返回一个字符串。
onPostExecute(),接收返回的字符串并显示在界面上(UI)。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

;


// AsyncTask的实现类用来从stackoverflow.com下载XML数据源.
private class DownloadXmlTask extends AsyncTask<String, Void, String> {
       @Override
       protected String doInBackground(String... urls) {
             try {
                   return loadXmlFromNetwork(urls[0]);
             } catch (IOException e) {
             return getResources().getString(R.string.connection_error);
       } catch (XmlPullParserException e) {
       return getResources().getString(R.string.xml_error);
}
}

@Override
protected void onPostExecute(String result) {
       setContentView(R.layout.main);
       // 通过WebView在UI上显示HTML字符串
       WebView myWebView = (WebView) findViewById(R.id.webview);
       myWebView.loadData(result, "text/html", null);
}
}

;


下方的loadXmlFromNetwork在DownloadXmlTask类被执行,其主要执行以下:

1.实例化一个StackOverflowXmlParser,并且创建一个Entry类型的List对象,以及title,url和summary变量来保存从XMl数据源中获取的相对应的字段。

2.调用downloadUrl方法,遍历数据并返回一个InputStream对象。

3.使用StackOverflowXmlParser解析InputStream,并将解析完毕的数据填充List变量entries。

4.处理entries对象,将解析的数据与HTML标签合并。

5.返回一个HTML字符串对象,并在AsyncTask类的onPostExecute方法中显示在主activity的界面上。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59

;


// 上传从stackoverflow.com获取的XML数据, 解析并合并其HTML标记,返回一个HTML字符串
private String loadXmlFromNetwork(String urlString) throws XmlPullParserException, IOException {
       InputStream stream = null;
       // 实例化解析器
       StackOverflowXmlParser stackOverflowXmlParser = new StackOverflowXmlParser();
       List<Entry> entries = null;
       String title = null;
       String url = null;
       String summary = null;
       Calendar rightNow = Calendar.getInstance();
       DateFormat formatter = new SimpleDateFormat("MMM dd h:mmaa");

       // 检查用户是否设置首选项
       SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
       boolean pref = sharedPrefs.getBoolean("summaryPref", false);

       StringBuilder htmlString = new StringBuilder();
       htmlString.append("<h3>" + getResources().getString(R.string.page_title) + "</h3>");
       htmlString.append("<em>" + getResources().getString(R.string.updated) + " " +
       formatter.format(rightNow.getTime()) + "</em>");

       try {
             stream = downloadUrl(urlString);
             entries = stackOverflowXmlParser.parse(stream);
             // 确保应用用完InputStream后关闭它
       } finally {
       if (stream != null) {
             stream.close();
       }
}

// StackOverflowXmlParser返回一个名为entries的List<Entry>对象
// 每一个Entry对象都表示在XML数据源中独立的项
// 本节处理entries对象,将每一个entry对象和HTML标签合并
// 每个entry都作为一个包含文本摘要的链接显示在UI上
for (Entry entry : entries) {
       htmlString.append("<p><a href='");
       htmlString.append(entry.link);
       htmlString.append("'>" + entry.title + "</a></p>");
       // 如果用户设置包含摘要文本,追加到显示中
       if (pref) {
             htmlString.append(entry.summary);
       }
}
return htmlString.toString();
}

// 给一个URL, 设置连接并获取输入流
private InputStream downloadUrl(String urlString) throws IOException {
       URL url = new URL(urlString);
       HttpURLConnection conn = (HttpURLConnection) url.openConnection();
       conn.setReadTimeout(10000 /* milliseconds * /);
       conn.setConnectTimeout(15000 /* milliseconds * /);
       conn.setRequestMethod("GET");
       conn.setDoInput(true);
       // 启动查询
       conn.connect();
       InputStream stream = conn.getInputStream();
}

;




赞(0)    操作        顶端 
总帖数
1
每页帖数
101/1页1
返回列表
发新帖子
请输入验证码: 点击刷新验证码
您需要登录后才可以回帖 登录 | 注册
技术讨论