Главная » Silverlight » Службы, возвращающие данные XML

0

Один из примеров использования службы REST — сайт Flickr. Введенные пользова­телем параметры добавляются в конец адреса URL в качестве аргументов строки запро­са. Веб-служба Flickr возвращает ответ в формате XML.

На рис. 20.4 показана страница Flickr, позволяющая задать ключевое слово поиска и выводящая список изображений, определяемый заданным ключевым словом.

Рис. 20.4. Получение изображений от сайта Flickr

В данном примере строка запроса к сайту Flickr содержит следующие аргументы: ме­тод (указывает на тип выполняемой операции); ключевое слово поиска; количество воз­вращаемых рисунков; уникальный идентификатор api_key, предоставляющий доступ к службе Flickr. Можно добавить дополнительные аргументы, позволяющие получить не­сколько страниц, сортировать и фильтровать данные и т.д. Подробная информация о веб- службах Flickr приведена на странице www. flickr. com/services/api.

Совет. Сайт Flickr предоставляет несколько способов вызова веб-служб. В данном примере простой способ REST используется для демонстрации обработки данных XML в приложении Silverlight. При создании реального приложения, обращающегося к сайту Flickr, легче применить интерфейс SOAP, сгенерировав необходимый код с помощью программы Visual Studio.

Ниже приведен запрос, возвращающий страницу, показанную на рис. 20.4.

http://арі.flickr.com/services/rest/?WWW

method=flickr.photos.search&tags=frog&api_key=…&perpage=3

Все входные параметры передаются в строке URL, поэтому нет необходимости вклю­чать что-либо в тело запроса и вместо WebRequest можно применить более простой класс WebClient. Ниже приведен код, создающий строку запроса к сайту Flickr и запу­скающий асинхронную операцию получения результата.

private void cmdGetData_Click (object sender, RoutedEventArgs e)

{

WebClient client = new WebClient (); Uri address = new

Uri("http://api.flickr.com/services/rest/?" + "method=flickr.photos.search" + "&tags=" + HttpUtility.UrlEncode(txtSearchKeyword.Text) + "4api_key=…" + "&perpage=3");

client.DownloadStringCompleted += client_DownloadStringCompleted; client.DownloadStringAsync(address);

)

Статический метод HttpUtility .UrlEncode () обеспечивает отсутствие в строке за­проса символов, не поддерживаемых спецификацией URL. Метод заменяет запрещен­ные символы соответствующими представлениями.

Результат возвращается как одна длинная строка, содержащая код XML в следую­щем формате.

<?xml version="l.0" encoding="utf-8" ?> <rsp stat="ok"> <photos page="l" pages="1026" perpage="100" total="l02577">

<photo id="2519140273" owner="85463968@N00" secret="9d215alb8b" server="2132"

farm="3" title="He could hop in, but couldn’t hop out" ispublic="l" isfriend="0" isfamily="0" /> <photo id="2519866774" owner="72063229@N00" secret="05bccd89cd" server="2353" farm="3" title="Small Frog on a Leaf" ispublic="l" isfriend="0" isfamily="0" />

</photos> </rsp>

Для синтаксического разбора этого кода необходимо загрузить весь документ в но­вый объект XDocument. Класс XDocument предоставляет два полезных статических метода. Метод Load() извлекает содержимое из потока XmlReader, а метод Parse () — из строки. При возникновении события завершения загрузки строки WebClient. DownloadStringCompleted запускается метод Parse О .

XDocument document = XDocument.Parse(e.Result) ;

Получив объект XDocument, можно извлечь из него информацию, применив одну из двух стратегий. Во-первых, можно пройти по коллекции элементов и атрибутов XDocument, представленных как объекты XElement и XAttribute. Во-вторых, можно при­менить выражение LINQ, которое извлекает содержимое XML и преобразует его в объ­ектное представление. В следующих разделах демонстрируются обе стратегии.

Навигация по объекту XDocument

Каждый объект XDocument содержит коллекцию объектов XNode. Абстрактный класс XNode является базовым для более специальных классов XElement, XText и XComment, которые представляют элементы, текст и комментарии. Атрибуты интерпретируются не как узлы, а как пары "имя-значение", подключенные к элементам.

Имея объект XDocument, можно пройти по дереву узлов с помощью свойств и методов класса XElement. В табл. 20.1 приведены наиболее полезные методы.

Таблица 20.1. Наиболее важные методы класса XElement

Метод

Описание

Attributes()

Получение коллекции объектов XAttribute, принадлежащих элементу

Attribute ()

Получение объекта XAttribute с заданным именем

Elements()

Получение коллекции объектов XElement, вложенных в данный элемент; каждый объект XElement в свою очередь тоже может содержать вложенные элементы; если задать имя дескриптора (не обязательно), будут возвращены только элементы заданного типа

Element()

Получение элемента XElement с заданным именем; если элемент не найден, возвращается значение null

Nodes ()

Получение объектов XNode, вложенных в данный элемент; в результат включается все содержимое, в том числе комментарии

Важно учитывать, что объект XDocument предоставляет вложенные элементы по­средством методов, а не свойств. Это дает возможность фильтровать элементы, возвра­щая только нужные. Например, метод XDocument .Elements () имеет две перегруженные версии. Если вызвать его без параметров, будут возвращены все дочерние элементы. Если же задать имя элемента через строковый параметр, будут возвращены только до­черние элементы с заданным именем.

В рассматриваемом примере верхнеуровневый элемент имеет имя <rsp>. Следовательно, его можно извлечь так.

XElement element = document.Element("rsp") ;

Из <rsp> нужно извлечь элемент <photos>.

XElement rspElement = document.Element("rsp"); XElement photosElement = element.Element("photos");

Эту операцию можно выполнить с помощью одного оператора, вместо двух.

XElement photosElement =

document.Element("rsp").Element("photos");

Из <photos> нужно извлечь элементы <photo>. Извлечение выполняется с помощью метода Elements (), потому что элементов <photo> много. Передавать методу имя эле­мента photo не нужно, потому что в текущем элементе есть только элементы <photo>.

IEnumerable<XElement> elements =

document.Element("rsp").Element("photos").Elements ();

Вся необходимая информация находится в атрибутах элементов <photo>. Чтобы по­лучить изображение Flickr (которое будет выведено в элементе Image), нужно создать правильный адрес URL.

private void client_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)

{

XDocument document = XDocument.Parse(e.Result);

// Очистка списка images.Items.Clear ();

// Обработка каждого элемента <photo> foreach (XElement element in

document.Element("rsp").Element("photos").Elements())

{

// Получение атрибутов и объединение их значений // для создания адресов URL изображений string imageUrl = String.Format ( "http://farm(0}.static.flickr.com/{1}/{2}_{3}_m.jpg", (string)element.Attribute("farm") , (string)element.Attribute("server"), (string)element.Attribute("id"), (string)element.Attribute("secret") ) ;

// Создание объекта Image, выводящего изображение

Image img = new Image () ;

img.Stretch = Stretch.Uniform;

img.Width = 200; img.Height = 200;

img.Margin = new Thickness(10) ;

img.Source = new Bitmaplmage(new Uri(imageUrl));

// Добавление элемента Image в список images.Items.Add(img);

}

Совет. Наиболее легкий способ извлечения значений из объектов XAttribute и XElement состоит в их приведении к нужному типу. В предыдущем примере все атрибуты интерпретируются как строковые значения.

Выше упомянуто об использовании методов Element () и Elements () для фильтрации элементов с заданными именами. Однако оба метода проникают лишь на один уровень вглубь. Классы XDocument и XElement предоставляют два метода, проникающих глубже: Ancestors О и Descendants (). Метод Ancestors () находит все объекты XElement, со­держащие текущий элемент на любой глубине. Метод Descendants () находит все объ­екты XElement, вложенные в текущий элемент на любой глубине. С помощью метода Descendants () оператор

foreach (XElement element in

document.Element("rsp").Element("photos").Elements())

можно записать следующим образом.

foreach (XElement element in document.Descendants("photo"))

Классы XDocument и XElement очень эффективны. Они предоставляют много средств навигации. Например, свойства FirstNode, LastNode, NextNode, PreviousNode и Parent позволяют быстро перейти от одного узла к другому. Методы ElementsAfterSelf () и ElementsBeforeSelf () позволяют найти узлы на том же уровне, что и текущий узел. Существуют также методы, предназначенные для манипулирования структурой доку­мента (см. далее).

Запросы к объекту XDocument средствами LINQ

Методы Elements (), Element (), Ancestors () и Descendants () используются для про­никновения в объект XDocument с целью извлечения нужного содержимого. Однако в не­которых ситуациях необходимо преобразовать структуру содержимого. Например, иногда нужно извлечь информацию из разных элементов, а затем объединить ее в более простую структуру. Для этого нужно обработать объект XDocument с помощью выражения LINQ.

Выражения LINQ (см. главу 16) манипулируют объектами, реализующими интер­фейс IEnumerable<T>. Классы XDocument и XElement предоставляют несколько методов извлечения коллекции IEnumerable<T> элементов, включая рассмотренные выше мето­ды Elements () и Descendants ().

После размещения коллекции в выражении LINQ к ней можно применять операто­ры LINQ. С их помощью можно группировать, фильтровать и сортировать извлекаемые данные.

Приведенный ниже оператор выбирает из документа XML все элементы <photo> с помощью метода Descendants О , извлекает значения наиболее важных атрибутов ‘ и устанавливает их как свойства объекта.

var photos = from results in document.Descendants("photo")

select new <

Id = (string)results.Attribute("id"), Farm = (string)results.Attribute("farm"), Server = (string)results.Attribute("server"), Secret = (string)results.Attribute ("secret")

};

В данном операторе используются анонимные типы LINQ. Выражение LINQ гене­рирует коллекцию объектов динамически определяемого типа, содержащего заданные свойства.

Компилятор С# создает определение класса. В коде нет ссылки на определение ди­намически сгенерированного класса, поэтому для определения коллекции photos ис­пользуется ключевое слово var. Код все же строго типизирован, потому что класс фак­тически существует. Единственное отличие от обычных классов состоит в том, что он сгенерирован автоматически во время компиляции. В других местах кода можно про­ходить по элементам коллекции photos, взаимодействуя со свойствами динамически сгенерированного типа для создания элементов Image.

foreach (var photo in photos)

(

url = string.Format("http://farm{0}.static.flickr.^ com/{1}/{2}_{3}_m.jpg", photo.farm, photo.server, photo.id, photo.secret);

Данный способ отображения части документа XML на новый класс называется про­ектированием (projection). Проектирование часто используется с анонимными типами для решения "одноразовой" задачи, когда нет необходимости использовать такое же группирование данных в других местах приложения. Проектирование можно приме­нить также для создания экземпляра пользовательского класса. Это наиболее удобный способ связывания данных со сгенерированным объектом.

Чтобы лучше понять принципы работы проектирования, рассмотрим альтернатив­ный способ создания страницы, показанной на рис. 20.4. Вместо создания каждого элемента Image вручную, определим шаблон данных, который связывает объекты, из­влекает информацию из URL и применяет ее в элементах Image.

CListBox x:Name="images">

<ListBox.ItemTemplate> <DataTemplate> <Image Stretch="Uniform" Width="200" Height="200" Margin="5" Sources"{Binding ImageUrl}"></Image> </DataTemplate>

</ListBox.ItemTemplate>

</ListBox>

Необходим также пользовательский класс, предоставляющий свойство ImageUrl (и другие свойства).

public class Flickrlmage

{

public string imageUrl { get; set; }

}

Для создания коллекции объектов Flickrlmage используется выражение LINQ.

var photos = from results in document.Descendants("photo")

select new Flickrlmage

{

imageUrl = String.Format( "http: Hi arm{ 0}. static. flickr. com/ {1} / {2 >_{ 3 >_rn .jpg", (string)results.Attribute("farm"), (string)results.Attribute("server"), (string)results.Attribute("id"), (string)results.Attribute("secret") )

};

images.ItemsSource = photos;

Данный способ обеспечивает минимальное количество кода и наиболее простое решение задачи.

Класс XDocument и пространства имен

Класс XDocument предоставляет элегантный способ работы с пространствами имен. Необходимо всего лишь до­бавить имя пространства имен в фигурных скобках перед именем элемента. Например, если нужно найти элемент

<photos> в пространстве имен http: //www. somecompany. com/PhotoMarkup, замените оператор

XElement photosElement = element.Element("photos"); оператором

XElement photosElement = element.Element( "{http://www.somecompany.com/PhotoMarkup}photos");

Чтобы код был чище, примените класс XNamespace.

XNamespace ns = "http://www.somecompany.com/DVDList"; XElement photosElement = element.Element(ns + "photos");

Таким способом пространство имен можно определить один раз, а затем повторно использовать при необходимости сослаться на элемент в данном пространстве имен. Этот же способ можно использовать для размещения элемента в некотором пространстве имен. Не забывайте только, что в большинстве языков на основе XML в пространствах имен размещаются элементы, но не атрибуты.

Источник: Мак-Дональд, Мэтью. Silverlight 3 с примерами на С# для профессионалов. : Пер. с англ. —- М. : ООО «И.Д. Вильяме», 2010. — 656 с. : ил. — Парал. тит. англ.

По теме:

  • Комментарии