уважаемые посетители блога, если Вам понравилась, то, пожалуйста, помогите автору с лечением. Подробности тут.

Во истину глаголят «Спешка нужна при ловле блох». Вот и я, начиная работу над Google Celenfar API для Delphi поторопился, забыв эту пословицу. Думал обойтись малой кровью, как при работе с FeedBurner API — разобрать пару XML-документов и сформировав на выходе пару записей. Ан не тут то было. Дойдя до работы с событиями календаря понял, что каждый раз парсить по-разному в сущности однотипный документ фида дело лишнее и не нужное.  Можно, конечно, оставить всё как есть и продолжать методом тыка определять содержимое документа, но мне лень :) Да и толку от такого модуля будет мало. Поэтому решил подойти к вопросу с начала, а не с неизвестно какого боку, как я начал. И первое, что решил сделать — это поработать с  Google Data Protocol с самого начала, т.к. собственно на нём всё держится и с использованием этого протокола работают все API Google.Для данных API форматом по умолчанию является Atom. Дополнительно мы можем использовать, например, формат RSS для чтения и отправки данных. При этом модель предоставления данных будет такой же самой, но будут небольшие изменения в именах элементов.

Любой фид  (Feed) Google может содержать следёющие элементы:

  • Title — заголовок
  • ID — идентификатор фида, например, для событий календария id — это ссылка на RSS-канал событий.
  • HTML Link — ссылка фида. Каждый элемент Link в XML-документе содержит атрибуты rel, href и type. Feed может содержать несколько ссылок.
  • Description — описание фида. В зависимости от формата данных описание может содержаться в элементах subtitle или description
  • Language — язык фида
  • Copyright — авторское право
  • Author — автор фида. При этом дочерними элементами узла являются name (имя) и email (электронная почта) автора.
  • Last Update Date (элемент updated) — дата последнего изменения фида
  • Generator — данные о генераторе фида.
  • Icon, Logo — ссылка на иконку фида.

Любой из перечисленных элементов может отсутствовать.  Далее каждый фид может содержать неограниченное количество элементов entry, у каждого из которых также определен «стандартный» набор элементов. В этих же элементах entry располагаются и другие данные, например, часовой пояс календаря, местоположение, цвет и т.д. Кроме того в этих элементах могу содержаться ссылки на другие фиды и фиды целиком, например, элемент entry может содержать в себе фид с комментариями пользователей и т.д.  И каждый раз разбирать по новой такой фид, как я уже говорил, мягко говоря не улыбает.

Поэтому, первый шаг, который я решил сделать при работе с протоколом — это научиться загружать нужный мне feed, читать его основные элементы и определять наличие элементов entry.

Читать будем всё, в т.ч. типы ссылок фида, uri генератора и т.д. Поэтому прежде всего определим типы данных для отдельных элементов фида. Так элемент фида link может содержать три атрибута rel, type и href поэтому этот элемент можно представить, например в виде записи:

type
TLinkElement = record
  rel: string;
  typ: string;
  href: string;
end;

Никакого текстового содержания у этого элемента нет. Элемент Author может содержать два текстовых узла name и email, чтобы держать эти анные вместе я решил определить элемент Author как:

type
TAuthorElement = record
  Email: string;
  Name : string;
end;

Элемент Generator может содержать атрибуты version и uri, а тексовое содержимое узла — название генератора:

type
TGeneratorElement = record
  varsion: string;
  uri: string;
  name: string;
end;

Остальные элементы у Feed — это текстовые узлы XML-документа, содержащие определенную информацию. Остается определиться с тем, как хранить ссылки фида, т.к. их количество может меняться. Выделять в классе N-ное количество полей — не вариант, поэтому я решил представить все ссылки фида в виде списка TList:

type
TLinkElementList = class(TList)
private
  procedure SetRecord(index: Integer; Ptr: PLinkElement);
  function GetRecord(index: Integer): PLinkElement;
public
  constructor Create;
  procedure Clear;
  destructor Destroy; override;
  property LinkElement[i: Integer]: PLinkElement read GetRecord write SetRecord;
end;

Этот список аналогичен тому, который я рассматривал в посте про вспомогательный модуль.

В итоге заготовка под класс Feed’а получилась такой:

type
TFeed = class
private
  FDocument       : IXMLDocument;
  FEtag           : string;
  FTitle          : string;
  FID             : string;
  FHTMLLinks      : TLinkElementList;
  FDescription    : string;
  FLanguage       : string;
  FCopyright      : string;
  FAuthor         : TAuthorElement;
  FUpdateDate     : TDateTime;
  FCategory       : TCategoryElement;
  FGenerator      : TGeneratorElement;
  FIcon           : string;
  FLogo           : string;
  FEntrys         : array of TEntryElemet;
  procedure GetBasicElements;
public
  constructor Create;
  procedure LoadFeed(const cLinkURL: string; ExtendedHeaders:  TStringList);
  property ETag: string read FEtag write FEtag;
  property Title: string read FTitle write FTitle;
  property Id: string read FID write FID;
  property Description: string read FDescription write FDescription;
  property Language : string read FLanguage write FLanguage;
  property Copyright: string read FCopyright write FCopyright;
  property UpdateDate : TDateTime read FUpdateDate write FUpdateDate;
  property Category : TCategoryElement read FCategory write FCategory;
  property Icon : string read FIcon write FIcon;
  property Logo : string read FLogo write FLogo;
  property HTMLLink : TLinkElementList read FHTMLLinks;
end;

В Create создаем пустой список:

constructor TFeed.Create;
begin
  inherited Create;
  FHTMLLinks:=TLinkElementList.Create;
end;

Теперь пробуем загрузить како-нибудь фид с данным и прочитать его содержимое. Я решил далеко не ходить и воспользоваться фидом событий своего календаря.

Загрузку фида будем осуществлять с помощью класса THTTPSender, рассмотренного в первом посте про Google Celendar API (кстати класс немного изменился и вырос, потом расскажу, что там и к чему).

Метод LoadFeed содержит следующие входные параметры:

cLinkURL — ссылка на фид

ExtendedHeaders — список дополнительных заголовков запроса. Используется тогда, когда необходимо подтвердить авторизацию в сервисе.

Сам метод выглядит так:

procedure TFeed.LoadFeed(const cLinkURL: string; ExtendedHeaders: TStringList);
begin
  with THTTPSender.Create do
    begin
      if ExtendedHeaders.Count>0 then
        Headers.Assign(ExtendedHeaders);
      Method:='GET';
      FDocument:=NewXMLDocument();
      FDocument.LoadFromXML(PostRequest(cLinkURL));
      if FDocument.IsEmptyDoc then
        ShowMessage('Загрузка не удалась')
      else
        GetBasicElements
    end;
end;

То есть получаем документ, грузим его в XML Document и если документ оказывается не пустой, т.е. не было никаких ошибок соединения, то пробуем чиать данные фида методом  GetBasicElements.

При этом, есть один тонкий момент, который будет доработан, когда THTTPSender будет работать в полную силу. Момент заключается в том, что при любом ответе сервера документ будет не пустой, т.к. в теле ответа будет содержаться html-код с описанием ошибки. Поэтому, если Вы решите (вдруг) воспользоваться приведенным выше кодом, то пока советую использовать для отправки/получения данных какую-нибудь готовую библиотеку, например, Synapse или Indy.

Сам метод GetBasicElements достаточно прост и работа его заключается в том, чтобы прочитать все элементы фида и сохранить их в соответствующие поля. Выглядит он так (часть метода):

procedure TFeed.GetBasicElements;
var i:integer;
    Root: IXMLNode;
    LinkElement: PLinkElement;
begin
if FDocument.IsEmptyDoc then Exit;
Root:=FDocument.DocumentElement;
if Root.Attributes['gd:etag']<>null then
  FEtag:=Root.Attributes['gd:etag'];
for i:=0 to Root.ChildNodes.Count - 1 do
  begin
    with Root.ChildNodes.Get(i)do
      begin
        if LowerCase(NodeName)='title' then
          FTitle:=Text
        else
         [...]
        else
         if LowerCase(NodeName)='link' then
           begin
             New(LinkElement);
             with LinkElement^ do
               begin
                 if Attributes['rel']<>null then
                   rel:=Attributes['rel'];
                 if Attributes['type']<>null then
                   typ:=Attributes['type'];
                 if Attributes['href']<>null then
                   href:=Attributes['href'];
               end;
             FHTMLLinks.Add(LinkElement);
           end
         else
           [...]
      end;
  end;
end;

В следующий раз планирую доработать THTTPSender и разобрать детально элементы entry, т.к. с ними придётся очень часто иметь дело в работе с Google Celendar API.

0 0 голоса
Рейтинг статьи
уважаемые посетители блога, если Вам понравилась, то, пожалуйста, помогите автору с лечением. Подробности тут.
Подписаться
Уведомить о
0 Комментарий
Межтекстовые Отзывы
Посмотреть все комментарии