【Android Basic Training - 02】基本网络操作 [ Lesson 3 - 详解如何解析XML数据 ]

Parsing XML Data [解析XML数据]
  • Extensible Markup Language (XML) . [关于什么是XML就不多介绍了, 它与JSON是最常用到的]. 很多网站或博客上都提供XML feed来记录更新的信息,以便用户进行订阅读取。
  • 那么上传[?]与解析XML数据就成了app的一个常见的功能。 这一课会介绍如何解析XML文档并使用他们的数据。[关于如何解析JSON数据,请参考【Android】解析Json 数据示例]
  • [?]这里很奇怪,为什么是Upload,看文章最后一段代码示例的注释,应该是Download才对,这个地方很有可能是Google编辑写错了,有不同观点的还望指正]

Choose a Parser [选择一个解析器]

Analyze the Feed [分析Feed]

  • 解析一个feed的第一步是决定需要获取哪些字段。这样解析器才去抽取出那些需要的字段而忽视剩下的。
  • 下面一段章节概览Sample app中截取的一段代码示例.
<?xml version="1.0" encoding="utf-8"?> 
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:creativeCommons="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 scheme="http://stackoverflow.com/feeds/tag?tagnames=android&sort=newest/tags" term="android"/>
    <category scheme="http://stackoverflow.com/feeds/tag?tagnames=android&sort=newest/tags" term="file"/>
    <author>
      <name>cliff2310</name>
      <uri>http://stackoverflow.com/users/1128925</uri>
    </author>
    <link rel="alternate" href="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>
  • 在sample app中抽取了entry标签与它的子标签title,link, summary.

Instantiate the Parser [实例化解析器]

下一步就是实例化一个parser并开始解析的操作。请看下面的示例:
public class StackOverflowXmlParser {
  // We don't use namespaces
  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 [读取Feed]

ThereadFeed()实际上并没有处理feed的内容。它只是在寻找一个 "entry"的标签作为递归(recursively)处理整个feed的起点。如果一个标签它不是"entry", readFeed()方法会跳过它. 当整个feed都被递归处理后,readFeed()会返回一个包含了entry标签(包括里面的数据成员)的List
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();
    // Starts by looking for the entry tag
    if (name.equals("entry")) {
      entries.add(readEntry(parser));
    } else {
      skip(parser);
    }
  } 
  return entries;
}

Parse XML [解析XML]

解析步骤如下:

  1. 正如在上面“ 分析Feed”所说的, 判断出你想要的tag。这个example抽取了entry标签与它的内部标签title,link, summary.
  2. 创建下面的方法:

    • 为每一个你想要获取的标签创建一个 "read" 方法。例如readEntry(),readTitle() 等等. 解析器从input stream中读取tag . 当读取到entry,title,link或者summary 标签时,它会为那些标签调用相应的方法,否则,跳过这个标签。
    • 为每一个不同的标签的提取数据方法进行优化,例如:
      • 对于titleandsummarytags, 解析器调用readText(). 通过调用parser.getText().来获取返回数据。
      • 对于linktag, 解析器先判断这个link是否是我们想要的类型,然后再读取数据。可以使用parser.getAttributeValue()来获取返回数据。
      • 对于entrytag, 解析起调用readEntry(). 这个方法解析entry的内部标签并返回一个带有title,link, andsummary 数据成员的Entry对象。
    • 一个帮助方法:skip(). 关于这部分的讨论,请看下面一部分内容:Skip Tags You Don't Care About

下面的代码演示了如何解析 entries, titles, links, 与 summaries.

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;
  }
}
 
// Parses the contents of an entry. If it encounters a title, summary, or link tag, hands them off
// to their respective "read" methods for processing. Otherwise, skips the tag.
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);
}

// Processes title tags in the feed.
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;
}
 
// Processes link tags in the feed.
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;
}

// Processes summary tags in the feed.
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;
}

// For the tags title and summary, extracts their text values.
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 [跳过你不在意标签]

  • 下面演示解析器的skip()方法:

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;
    }
  }
}

  • 上面这个方法是如何工作的呢?【下面的就不翻译了】
    • It throws an exception if the current event isn't aSTART_TAG.
    • It consumes theSTART_TAG, and all events up to and including the matchingEND_TAG.
    • To make sure that it stops at the correctEND_TAGand not at the first tag it encounters after the originalSTART_TAG, it keeps track of the nesting depth.
  • 因此如果目前的标签有子标签, depth的值就不会为 0,直到解析器已经处理了所有位于START_TAGEND_TAG之间的事件。例如,看解析器如何跳过<author>标签,它有2个子标签,<name><uri>:
    • The first time through thewhileloop, the next tag the parser encounters after<author>is theSTART_TAGfor<name>. The value fordepthis incremented to 2.
    • The second time through thewhileloop, the next tag the parser encounters is theEND_TAG</name>. The value fordepthis decremented to 1.
    • The third time through thewhileloop, the next tag the parser encounters is theSTART_TAG<uri>. The value fordepthis incremented to 2.
    • The fourth time through thewhileloop, the next tag the parser encounters is theEND_TAG</uri>. The value fordepthis decremented to 1.
    • The fifth time and final time through thewhileloop, the next tag the parser encounters is theEND_TAG</author>. The value fordepthis decremented to 0, indicating that the<author>element has been successfully skipped.

Consume XML Data [使用XML数据]

示例程序是在AsyncTask中获取与解析XML数据的。当获取到数据后,程序会在main activity(NetworkActivity)里面进行更新操作。

在下面示例代码中,loadPage()方法做了下面的事情:

  • 初始化一个带有URL地址的String变量,用来订阅XML feed。
  • 如果用户设置与网络连接都允许,会触发new DownloadXmlTask().execute(url). 这会初始化一个新的DownloadXmlTask(AsyncTasksubclass)对象并且开始执行它的execute()方法。
public class NetworkActivity extends Activity {
  public static final String WIFI = "Wi-Fi";
  public static final String ANY = "Any";
  private static final String URL = "http://stackoverflow.com/feeds/tag?tagnames=android&sort=newest";
 
  // Whether there is a Wi-Fi connection.
  private static boolean wifiConnected = false; 
  // Whether there is a mobile connection.
  private static boolean mobileConnected = false;
  // Whether the display should be refreshed.
  public static boolean refreshDisplay = true; 
  public static String sPref = null;

  ...
   
  // Uses AsyncTask to download the XML feed from stackoverflow.com.
  public void loadPage() { 
   
    if((sPref.equals(ANY)) && (wifiConnected || mobileConnected)) {
      new DownloadXmlTask().execute(URL);
    }
    else if ((sPref.equals(WIFI)) && (wifiConnected)) {
      new DownloadXmlTask().execute(URL);
    } else {
      // show error
    } 
  }
下面是 DownloadXmlTask是怎么工作的:

// Implementation of AsyncTask used to download XML feed from stackoverflow.com.
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);
    // Displays the HTML string in the UI via a WebView
    WebView myWebView = (WebView) findViewById(R.id.webview);
    myWebView.loadData(result, "text/html", null);
  }
}
下面是 loadXmlFromNetwork 是怎么工作的:

// Uploads XML from stackoverflow.com, parses it, and combines it with
// HTML markup. Returns HTML string.【这里可以看出应该是Download】
private String loadXmlFromNetwork(String urlString) throws XmlPullParserException, IOException {
  InputStream stream = null;
  // Instantiate the parser
  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");
    
  // Checks whether the user set the preference to include summary text
  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);
  // Makes sure that the InputStream is closed after the app is
  // finished using it.
  } finally {
    if (stream != null) {
      stream.close();
    } 
  }
  
  // StackOverflowXmlParser returns a List (called "entries") of Entry objects.
  // Each Entry object represents a single post in the XML feed.
  // This section processes the entries list to combine each entry with HTML markup.
  // Each entry is displayed in the UI as a link that optionally includes
  // a text summary.
  for (Entry entry : entries) {    
    htmlString.append("<p><a href='");
    htmlString.append(entry.link);
    htmlString.append("'>" + entry.title + "</a></p>");
    // If the user set the preference to include summary text,
    // adds it to the display.
    if (pref) {
      htmlString.append(entry.summary);
    }
  }
  return htmlString.toString();
}

// Given a string representation of a URL, sets up a connection and gets
// an input stream. 
【关于Timeout具体应该设置多少,可以借鉴这里的数据,当然前提是一般情况下】
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 ); // Starts the query conn . connect (); InputStream stream = conn . getInputStream (); }

学习自:http://developer.android.com/training/basics/network-ops/xml.html,请多指教,谢谢!

转载请注明作者与出自:http://blog.csdn.net/kesenhoo,谢谢配合!





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值