Статьи

Android для Windows Phone 8: анализ данных XML

Теперь, когда ваше приложение может подключаться к сети и управлять использованием сети, пришло время начать работу с данными. На сегодняшний день в Интернете используются два наиболее распространенных формата данных: расширяемый язык разметки (XML) и объектная нотация JavaScript (JSON). Веб-сайты, которые часто обновляют свое содержимое, например блоги, часто предоставляют XML-канал, чтобы приложения могли следить за изменениями содержимого. Этот раздел покажет вам, как анализировать XML-документ и использовать его данные в ваших приложениях.

Первое, что вам нужно сделать, это выбрать XML-фид, который вы хотите использовать в своем приложении. Для этого раздела будет использоваться лента из  StackOverflow.com  , ориентированная на Windows 8. Канал доступен по адресу http://stackoverflow.com/feeds/tag/windows-phone-8 .

После того, как вы определили канал, вы захотите проанализировать его, чтобы определить, какие поля представляют интерес для вашего приложения. Ниже приведен пример абстрактной формы   фида StackOverflow.com, который мы будем использовать:

<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:creativeCommons="http://backend.userland.com/creativeCommonsRssModule" xmlns:re="http://purl.org/atompub/rank/1.0">
<title type="text">active questions tagged windows-phone-8 - Stack Overflow</title>
...
<entry>
...
</entry>
<entry>
 <id>http://stackoverflow.com/q/00000000</id>
 <re:rank scheme="http://stackoverflow.com">0</re:rank>
 <title type="text">Windows Phone XML Data</title>
 <category scheme="http://stackoverflow.com/feeds/tag/windows-phone-8/tags" term="c#"/>
 <category scheme="http://stackoverflow.com/feeds/tag/windows-phone-8/tags" term="xml"/>
 <category scheme="http://stackoverflow.com/feeds/tag/windows-phone-8/tags" term="windows-8"/>
 <category scheme="http://stackoverflow.com/feeds/tag/windows-phone-8/tags" term="windows-runtime"/>
 <author>
  <name>anonymous</name>
  <uri>http://stackoverflow.com/users/000000</uri>
 </author>
 <link rel="alternate" href="http://stackoverflow.com/questions/00000000/windows-store-xml-data" />
 <published>1900-01-01T00:00:00Z</published>
 <updated>1900-01-01T00:00:00Z</updated>
 <summary type="html">
  <p>How do I use XML in Windows Phone apps?</p>
 </summary>
</entry>
<entry>
...
</entry>
...
</feed>

Для примера мы сосредоточимся на  элементах title ,  link и  summary  . Первым шагом является создание класса, который представляет данные, с которыми вы хотите работать в вашем приложении. Поскольку мы фокусируемся на элементах заголовка, ссылки и сводки, класс будет выглядеть следующим образом:

public class FeedEntry
{
 public string Title { get; set; }

 public string Link { get; set; }

 public string Summary { get; set; }
}

Следующим шагом является создание класса и метода, который будет отвечать за анализ:

public class StackOverflowXmlParser
{
 ...
}

Поскольку мы будем подключаться к интернет-ресурсу для получения данных XML, мы хотим сделать это асинхронно, чтобы избежать блокировки других операций. Как только мы получим данные и разберем их в списке объектов FeedEntry, мы вызовем событие, чтобы сообщить вызывающей стороне, что операция завершена. Это можно сделать с помощью следующего:

...
public event EventHandler<List<FeedEntry>> ParseCompleted;
...

Далее нам понадобится метод, который запускает асинхронную процедуру для получения данных и их анализа:

...
public void ParseAsync()
{
 var connectionState = GetConnectionState();

 if (connectionState == ConnectionState.WiFi || connectionState == ConnectionState.Ethernet)
 {
  var request = WebRequest.CreateHttp("http://stackoverflow.com/feeds/tag/windows-8");

  request.BeginGetResponse(BeginGetResponseCallback, request);
 }
}
...

Этот код делает пару вещей. Сначала он получает текущий тип интернет-соединения, которое имеет устройство. Если устройство подключено к соединению WiFi или Ethernet, оно приступает к созданию   объекта HttpWebRequest и вызывает его   метод BeginGetResponse . Это начинает асинхронный запрос к StackOverflow для конкретного интересующего нас фида. Первым параметром этого метода является обратный вызов, который будет вызван при получении ответа. Второй параметр вызова — это любое состояние, которое мы хотим использовать в обратном вызове. В этом случае мы хотим убедиться, что мы получаем доступ к исходному HttpWebRequest, поэтому мы просто пропускаем его.

Мы будем использовать обратный вызов для анализа данных. Первое, что мы хотим сделать в этом методе, — это создать список объектов FeedEntry для сохранения результатов.

...
private void BeginGetResponseCallback(IAsyncResult asyncResult)
{
 List<FeedEntry> results = new List<FeedEntry>();
 ...
}
...

Далее нам нужно получить поток ответов, содержащий данные, которые мы хотим проанализировать:

...
var request = (HttpWebRequest)asyncResult.AsyncState;

var response = request.EndGetResponse(asyncResult);

var responseStream = response.GetResponseStream();
...

Следующее, что нужно сделать, это определить метод, с помощью которого будет обрабатываться канал. Поскольку мы имеем дело с фидом XML, у нас есть три варианта в .NET:

  1. Пространство   имен System.Xml
  2. Пространство   имен System.Xml.Linq
  3. System.ServiceModel.Syndication  имен

Пространство имен System.Xml обеспечивает основанную на стандартах поддержку для обработки XML. Он дает вам немного контроля над тем, как вы обрабатываете XML, и позволяет вам использовать такие вещи, как схемы XSD, выражения XPath и преобразования XSLT. Это немного больше энергии, чем нам нужно для этой конкретной задачи. Класс  SyndicationFeed  в пространстве имен System.ServiceModel.Syndication фактически сделает нашу работу невероятно простой, если мы хотим работать со всеми полями в ленте. Однако, поскольку нас интересуют только три поля, мы будем использовать пространство имен System.Xml.Linq. Это пространство имен содержит классы для LINQ (Language INtegrated Query) to XML, который служит интерфейсом программирования XML в памяти, который обеспечивает простую и эффективную обработку документов XML.

Прежде всего, при работе с LINQ to XML необходимо создать несколько   объектов XName, которые представляют имена элементов, с которыми мы хотим работать. Эти имена должны включать локальные имена и имена пространств имен.

//Atom namespace
var atomNamespace = "http://www.w3.org/2005/Atom";

//Create the names of the XML elements including the Atom namespace
var entryXName = XName.Get("entry", atomNamespace);
var titleXName = XName.Get("title", atomNamespace);
var linkXName = XName.Get("link", atomNamespace);
var summaryXName = XName.Get("summary", atomNamespace);

Следующим шагом является загрузка данных XML в потоке ответов в новый экземпляр   класса XElement .

...
XElement root = XElement.Load(responseStream);
...
Android-совет
Это похоже на использование Android  HttpURLConnection  для получения и InputStream
private InputStream downloadXml() throws IOException {

 URL url = new URL('http://stackoverflow.com/feeds/tag/windows-8');

 HttpURLConnection conn = (HttpURLConnection) url.openConnection();

 conn.setRequestMethod("GET");

 conn.setDoInput(true);

 conn.connect();

 return conn.getInputStream();
}

Затем вы должны использовать вывод InputStream в качестве входного параметра в  методе setInput  класса  XmlPullParser  .

Как только объект XElement загружен, мы можем использовать LINQ для запроса только  элементов ввода и в XElement и сохранить их в списке:

//Load all "entry" elements from the XML into a list
var entries = (from e in root.Elements(entryXName) select e).ToList();

Далее мы начнем перебирать список записей. Первым шагом в итерации является создание нового объекта FeedEntry:

//Iterate all "entry" elements
foreach (var entry in entries)
{
 //Create a new FeedEntry
 var feedEntry = new FeedEntry();
 ...
}

Затем мы присвоим значение свойства Title FeedEntry значению элемента title:

//Get the title
var titleElement = (from e in entry.Elements(titleXName) select e).FirstOrDefault();

if (titleElement != null)
 feedEntry.Title = titleElement.Value;
Android-совет
Эквивалент Android будет вызывать метод  getText ()  класса XmlPullParser, чтобы получить значение текущего элемента.

Аналогичным образом мы присвоим значение свойства Link значению атрибута href элемента link:

//Get the link
var linkElement = (from e in entry.Elements(linkXName) select e).FirstOrDefault();

if (linkElement != null)
{
 var hrefAttribute = linkElement.Attributes("href").FirstOrDefault();

 if (hrefAttribute != null)
  feedEntry.Link = hrefAttribute.Value;
}
Android-совет
Чтобы получить значение атрибута в Android, вы должны использовать метод getAttribueValue ()  класса XmlPullParser.

Далее мы присвоим значение свойства Summary значению элемента summary и добавим объект в набор результатов, который будет возвращен вызывающему методу:

//Get the summary
if (summaryElement != null)
 feedEntry.Summary = WebUtility.HtmlDecode(summaryElement.Value);

//Add to the entry to the result set
results.Add(feedEntry);

Затем нам нужно вызвать событие, чтобы сообщить исходному вызывающему методу, что анализ завершен:

if (this.ParseCompleted != null)
 this.ParseCompleted(this, results);

Последний шаг — вызов   метода ParseAsync и ответ на событие ParseCompleted  .

public void GetXmlData()
{
 var parser = new StackOverlfowParser();

 parser.ParseCompleted += parser_ParseCompleted;

 parser.ParseAsync();
}

private void parser_ParseCompleted(object sender, List<FeedEntry> e)
{
 //do something with the results
}

Это оно! Ваше приложение Windows Phone теперь может работать с данными из Интернета.